summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2016-04-12 15:50:55 -0700
committer Alex Light <allight@google.com> 2016-04-13 21:12:42 +0000
commitb55f1ac873f9541f391625c13fe9129fbd38e74c (patch)
tree996e696a06437f93c6f87e773e76d9e3c6a4fe8a
parent336dd6a0989dafb356be5f689028d983b0931335 (diff)
Allow private methods in interfaces.
Private methods may be generated in interfaces during compilation of some default methods. Change the verifier to allow these methods. Bug: 27999840 Change-Id: Ib8120a8f6cb036021334d9af0ed78ae372974ecb
-rw-r--r--runtime/dex_file.h1
-rw-r--r--runtime/dex_file_verifier.cc16
-rw-r--r--runtime/verifier/method_verifier.cc18
-rwxr-xr-xtest/955-lambda-smali/build20
-rwxr-xr-xtest/975-iface-private/build20
-rw-r--r--test/975-iface-private/expected.txt4
-rw-r--r--test/975-iface-private/info.txt5
-rw-r--r--test/975-iface-private/smali/Iface.smali45
-rw-r--r--test/975-iface-private/smali/Main.smali71
-rwxr-xr-xtest/etc/default-build6
-rwxr-xr-xtest/run-test2
11 files changed, 196 insertions, 12 deletions
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 3a28422067..ce7f62acb5 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -57,6 +57,7 @@ class ZipArchive;
// TODO: move all of the macro functionality into the DexCache class.
class DexFile {
public:
+ static const uint32_t kDefaultMethodsVersion = 37;
static const uint8_t kDexMagic[];
static constexpr size_t kNumDexVersions = 2;
static constexpr size_t kDexVersionLen = 4;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 681c5f977f..3df4e98c84 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2465,7 +2465,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx,
GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
field_access_flags,
PrettyJavaAccessFlags(field_access_flags).c_str());
- if (header_->GetVersion() >= 37) {
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
return false;
} else {
// Allow in older versions, but warn.
@@ -2480,7 +2480,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx,
GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
field_access_flags,
PrettyJavaAccessFlags(field_access_flags).c_str());
- if (header_->GetVersion() >= 37) {
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
return false;
} else {
// Allow in older versions, but warn.
@@ -2628,12 +2628,16 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
// Interfaces are special.
if ((class_access_flags & kAccInterface) != 0) {
- // Non-static interface methods must be public.
- if ((method_access_flags & (kAccPublic | kAccStatic)) == 0) {
+ // Non-static interface methods must be public or private.
+ uint32_t desired_flags = (kAccPublic | kAccStatic);
+ if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ desired_flags |= kAccPrivate;
+ }
+ if ((method_access_flags & desired_flags) == 0) {
*error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (header_->GetVersion() >= 37) {
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
return false;
} else {
// Allow in older versions, but warn.
@@ -2686,7 +2690,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
*error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (header_->GetVersion() >= 37) {
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
return false;
} else {
// Allow in older versions, but warn.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 83da6b7b4e..d5319fdb0c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -790,9 +790,16 @@ bool MethodVerifier::Verify() {
} else if (method_access_flags_ & kAccFinal) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
return false;
- } else if (!(method_access_flags_ & kAccPublic)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
- return false;
+ } else {
+ uint32_t access_flag_options = kAccPublic;
+ if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ access_flag_options |= kAccPrivate;
+ }
+ if (!(method_access_flags_ & access_flag_options)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "interfaces may not have protected or package-private members";
+ return false;
+ }
}
}
}
@@ -3794,9 +3801,12 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
// Note: this check must be after the initializer check, as those are required to fail a class,
// while this check implies an IncompatibleClassChangeError.
if (klass->IsInterface()) {
- // methods called on interfaces should be invoke-interface, invoke-super, or invoke-static.
+ // methods called on interfaces should be invoke-interface, invoke-super, invoke-direct (if
+ // dex file version is 37 or greater), or invoke-static.
if (method_type != METHOD_INTERFACE &&
method_type != METHOD_STATIC &&
+ ((dex_file_->GetVersion() < DexFile::kDefaultMethodsVersion) ||
+ method_type != METHOD_DIRECT) &&
method_type != METHOD_SUPER) {
Fail(VERIFY_ERROR_CLASS_CHANGE)
<< "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
diff --git a/test/955-lambda-smali/build b/test/955-lambda-smali/build
new file mode 100755
index 0000000000..14230c2e1d
--- /dev/null
+++ b/test/955-lambda-smali/build
@@ -0,0 +1,20 @@
+#!/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 b/test/975-iface-private/build
new file mode 100755
index 0000000000..14230c2e1d
--- /dev/null
+++ b/test/975-iface-private/build
@@ -0,0 +1,20 @@
+#!/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/expected.txt b/test/975-iface-private/expected.txt
new file mode 100644
index 0000000000..908a8f2131
--- /dev/null
+++ b/test/975-iface-private/expected.txt
@@ -0,0 +1,4 @@
+Saying hi from class
+HELLO!
+Saying hi from interface
+HELLO!
diff --git a/test/975-iface-private/info.txt b/test/975-iface-private/info.txt
new file mode 100644
index 0000000000..d5a8d3f6d5
--- /dev/null
+++ b/test/975-iface-private/info.txt
@@ -0,0 +1,5 @@
+Smali-based tests for experimental interface private methods.
+
+This test cannot be run with --jvm.
+
+This test checks that synthetic private methods in interfaces work correctly.
diff --git a/test/975-iface-private/smali/Iface.smali b/test/975-iface-private/smali/Iface.smali
new file mode 100644
index 0000000000..a9a44d1bf4
--- /dev/null
+++ b/test/975-iface-private/smali/Iface.smali
@@ -0,0 +1,45 @@
+
+# /*
+# * 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.
+# */
+#
+# public interface Iface {
+# public default void sayHi() {
+# System.out.println(getHiWords());
+# }
+#
+# // Synthetic method
+# private String getHiWords() {
+# return "HELLO!";
+# }
+# }
+
+.class public abstract interface LIface;
+.super Ljava/lang/Object;
+
+.method public sayHi()V
+ .locals 2
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-direct {p0}, LIface;->getHiWords()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method private synthetic getHiWords()Ljava/lang/String;
+ .locals 1
+ const-string v0, "HELLO!"
+ return-object v0
+.end method
diff --git a/test/975-iface-private/smali/Main.smali b/test/975-iface-private/smali/Main.smali
new file mode 100644
index 0000000000..dbde20362a
--- /dev/null
+++ b/test/975-iface-private/smali/Main.smali
@@ -0,0 +1,71 @@
+# /*
+# * Copyright (C) 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# class Main implements Iface {
+# public static void main(String[] args) {
+# Main m = new Main();
+# sayHiMain(m);
+# sayHiIface(m);
+# }
+# public static void sayHiMain(Main m) {
+# System.out.println("Saying hi from class");
+# m.sayHi();
+# }
+# public static void sayHiIface(Iface m) {
+# System.out.println("Saying hi from interface");
+# m.sayHi();
+# }
+# }
+.class public LMain;
+.super Ljava/lang/Object;
+.implements LIface;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+ new-instance v0, LMain;
+ invoke-direct {v0}, LMain;-><init>()V
+
+ invoke-static {v0}, LMain;->sayHiMain(LMain;)V
+ invoke-static {v0}, LMain;->sayHiIface(LIface;)V
+
+ return-void
+.end method
+
+.method public static sayHiMain(LMain;)V
+ .locals 2
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "Saying hi from class"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-virtual {p0}, LMain;->sayHi()V
+ return-void
+.end method
+
+.method public static sayHiIface(LIface;)V
+ .locals 2
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "Saying hi from interface"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-interface {p0}, LIface;->sayHi()V
+ return-void
+.end method
diff --git a/test/etc/default-build b/test/etc/default-build
index 3d84821bf0..962ae38041 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -69,10 +69,13 @@ declare -A JACK_EXPERIMENTAL_ARGS
JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
+declare -A SMALI_EXPERIMENTAL_ARGS
+SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
+
while true; do
if [ "x$1" = "x--dx-option" ]; then
shift
- option="$1"
+ on="$1"
DX_FLAGS="${DX_FLAGS} $option"
shift
elif [ "x$1" = "x--jvm" ]; then
@@ -110,6 +113,7 @@ done
# Add args from the experimental mappings.
for experiment in ${EXPERIMENTAL}; do
JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
+ SMALI_ARGS="${SMALI_ARGS} ${SMALI_EXPERIMENTAL_ARGS[${experiment}]}"
done
if [ -e classes.dex ]; then
diff --git a/test/run-test b/test/run-test
index 01464cd6b6..013fc63e83 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,7 +46,7 @@ export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
export USE_JACK="true"
-export SMALI_ARGS="--experimental --api-level 23"
+export SMALI_ARGS="--experimental"
# If dx was not set by the environment variable, assume it is in the path.
if [ -z "$DX" ]; then