Allow invokevirtual, invokespecial, invokestatic on interface methods
Allow default and static methods on interfaces if
--min-sdk-version >= 24.
Test: make checkbuild
Change-Id: I6b617a37256bdb95f4c11e58fe2ebf08cf7aa324
diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java
index 0e77941..88225e6 100644
--- a/dx/src/com/android/dx/Version.java
+++ b/dx/src/com/android/dx/Version.java
@@ -21,5 +21,5 @@
*/
public class Version {
/** {@code non-null;} version string */
- public static final String VERSION = "1.12";
+ public static final String VERSION = "1.13";
}
diff --git a/dx/src/com/android/dx/cf/code/RopperMachine.java b/dx/src/com/android/dx/cf/code/RopperMachine.java
index d554d91..7ed6328 100644
--- a/dx/src/com/android/dx/cf/code/RopperMachine.java
+++ b/dx/src/com/android/dx/cf/code/RopperMachine.java
@@ -958,6 +958,10 @@
* on "invokespecial" as well as section 4.8.2 (7th
* bullet point) for the gory details.
*/
+ /* TODO: Consider checking that invoke-special target
+ * method is private, or constructor since otherwise ART
+ * verifier will reject it.
+ */
CstMethodRef ref = (CstMethodRef) cst;
if (ref.isInstanceInit() ||
(ref.getDefiningClass().equals(method.getDefiningClass()))) {
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
index 46ab4df..b858bb0 100644
--- a/dx/src/com/android/dx/cf/code/Simulator.java
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -654,32 +654,30 @@
machine.popArgs(frame, Type.OBJECT, fieldType);
break;
}
- case ByteOps.INVOKEINTERFACE: {
- /*
- * Convert the interface method ref into a normal
- * method ref.
- */
- cst = ((CstInterfaceMethodRef) cst).toMethodRef();
- // and fall through...
- }
+ case ByteOps.INVOKEINTERFACE:
case ByteOps.INVOKEVIRTUAL:
- case ByteOps.INVOKESPECIAL: {
- /*
- * Get the instance prototype, and use it to direct
- * the machine.
- */
- Prototype prototype =
- ((CstMethodRef) cst).getPrototype(false);
- machine.popArgs(frame, prototype);
- break;
- }
+ case ByteOps.INVOKESPECIAL:
case ByteOps.INVOKESTATIC: {
/*
- * Get the static prototype, and use it to direct
- * the machine.
+ * Convert the interface method ref into a normal
+ * method ref if necessary.
*/
+ if (cst instanceof CstInterfaceMethodRef) {
+ if (opcode != ByteOps.INVOKEINTERFACE) {
+ if (!dexOptions.canUseDefaultInterfaceMethods()) {
+ throw new SimException(
+ "default or static interface method used without --min-sdk-version >= 24");
+ }
+ }
+ cst = ((CstInterfaceMethodRef) cst).toMethodRef();
+ }
+ /*
+ * Get the instance or static prototype, and use it to
+ * direct the machine.
+ */
+ boolean staticMethod = (opcode == ByteOps.INVOKESTATIC);
Prototype prototype =
- ((CstMethodRef) cst).getPrototype(true);
+ ((CstMethodRef) cst).getPrototype(staticMethod);
machine.popArgs(frame, prototype);
break;
}
diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java
index 17038cd..b598d60 100644
--- a/dx/src/com/android/dx/command/Main.java
+++ b/dx/src/com/android/dx/command/Main.java
@@ -31,7 +31,7 @@
" [--dump-method=<name>[*]] [--verbose-dump] [--no-files] [--core-library]\n" +
" [--num-threads=<n>] [--incremental] [--force-jumbo] [--no-warning]\n" +
" [--multi-dex [--main-dex-list=<file> [--minimal-main-dex]]\n" +
- " [--input-list=<file>]\n" +
+ " [--input-list=<file>] [--min-sdk-version=<n>]\n" +
" [<file>.class | <file>.{zip,jar,apk} | <directory>] ...\n" +
" Convert a set of classfiles into a dex file, optionally embedded in a\n" +
" jar/zip. Output name must end with one of: .dex .jar .zip .apk or be a\n" +
@@ -47,6 +47,8 @@
" --input-list: <file> is a list of inputs.\n" +
" Each line in <file> must end with one of: .class .jar .zip .apk or be a\n" +
" directory.\n" +
+ " --min-sdk-version=<n>: Enable dex file features that require at least sdk\n" +
+ " version <n>.\n" +
" dx --annotool --annotation=<class> [--element=<element types>]\n" +
" [--print=<print types>]\n" +
" dx --dump [--debug] [--strict] [--bytes] [--optimize]\n" +
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
index 96704df..10189a7 100644
--- a/dx/src/com/android/dx/command/dexer/Main.java
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -1298,6 +1298,9 @@
*/
public boolean keepClassesInJar = false;
+ /** what API level to target */
+ public int minSdkVersion = DexFormat.API_NO_EXTENDED_OPCODES;
+
/** how much source position info to preserve */
public int positionInfo = PositionList.LINES;
@@ -1556,15 +1559,28 @@
maxNumberOfIdxPerDex = Integer.parseInt(parser.getLastValue());
} else if(parser.isArg(INPUT_LIST_OPTION + "=")) {
File inputListFile = new File(parser.getLastValue());
- try{
+ try {
inputList = new ArrayList<String>();
readPathsFromFile(inputListFile.getAbsolutePath(), inputList);
- } catch(IOException e) {
+ } catch (IOException e) {
context.err.println(
"Unable to read input list file: " + inputListFile.getName());
// problem reading the file so we should halt execution
throw new UsageException();
}
+ } else if (parser.isArg("--min-sdk-version=")) {
+ String arg = parser.getLastValue();
+ int value;
+ try {
+ value = Integer.parseInt(arg);
+ } catch (NumberFormatException ex) {
+ value = -1;
+ }
+ if (value < 1) {
+ System.err.println("improper min-sdk-version option: " + arg);
+ throw new UsageException();
+ }
+ minSdkVersion = value;
} else {
context.err.println("unknown option: " + parser.getCurrent());
throw new UsageException();
@@ -1665,6 +1681,7 @@
}
dexOptions = new DexOptions();
+ dexOptions.minSdkVersion = minSdkVersion;
dexOptions.forceJumbo = forceJumbo;
}
}
diff --git a/dx/src/com/android/dx/dex/DexOptions.java b/dx/src/com/android/dx/dex/DexOptions.java
index fbb7c5a..a54ebd3 100644
--- a/dx/src/com/android/dx/dex/DexOptions.java
+++ b/dx/src/com/android/dx/dex/DexOptions.java
@@ -38,8 +38,8 @@
*/
public boolean ALIGN_64BIT_REGS_IN_OUTPUT_FINISHER = ALIGN_64BIT_REGS_SUPPORT;
- /** target API level */
- public int targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES;
+ /** minimum SDK version targeted */
+ public int minSdkVersion = DexFormat.API_NO_EXTENDED_OPCODES;
/** force generation of jumbo opcodes */
public boolean forceJumbo = false;
@@ -48,6 +48,14 @@
* Gets the dex file magic number corresponding to this instance.
*/
public String getMagic() {
- return DexFormat.apiToMagic(targetApiLevel);
+ return DexFormat.apiToMagic(minSdkVersion);
+ }
+
+ /**
+ * Returns whether default and static interface methods are allowed. This became allowed as of
+ * Nougat (SDK version 24).
+ */
+ public boolean canUseDefaultInterfaceMethods() {
+ return minSdkVersion >= DexFormat.API_DEFAULT_INTERFACE_METHODS;
}
}