Add const-method-type support
Bug: 64891562
Change-Id: I889bf673cc942c0710dd9847b2074375690c96a6
diff --git a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
index bb31282..4e15eb9 100644
--- a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
+++ b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
@@ -456,6 +456,8 @@
return new InvokeCustomRange(high, stream, mapping);
case ConstMethodHandle.OPCODE:
return new ConstMethodHandle(high, stream, mapping);
+ case ConstMethodType.OPCODE:
+ return new ConstMethodType(high, stream, mapping);
default:
throw new IllegalArgumentException("Illegal Opcode: 0x" + Integer.toString(opcode, 16));
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
new file mode 100644
index 0000000..7e50926
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class ConstMethodType extends Format21c {
+
+ public static final int OPCODE = 0xff;
+ public static final String NAME = "ConstMethodType";
+ public static final String SMALI_NAME = "const-method-type";
+
+ ConstMethodType(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getProtosMap());
+ }
+
+ public ConstMethodType(int register, DexProto methodType) {
+ super(register, methodType);
+ }
+
+ public DexProto getMethodType() {
+ return (DexProto) BBBB;
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\"");
+ }
+
+ @Override
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ int index = BBBB.getOffset(mapping);
+ if (index != (index & 0xffff)) {
+ throw new InternalCompilerError("MethodType-index overflow.");
+ }
+ super.write(dest, mapping);
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) throws ApiLevelException {
+ builder.addConstMethodType(AA, (DexProto) BBBB);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
new file mode 100644
index 0000000..3e32bb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstMethodType extends ConstInstruction {
+
+ private final DexProto methodType;
+
+ public ConstMethodType(Value dest, DexProto methodType) {
+ super(dest);
+ dest.markNeverNull();
+ this.methodType = methodType;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public DexProto getValue() {
+ return methodType;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.ConstMethodType(dest, methodType));
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asConstMethodType().methodType == methodType;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return methodType.slowCompareTo(other.asConstMethodType().methodType);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "ConstMethodType has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " \"" + methodType + "\"";
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean isOutConstant() {
+ return true;
+ }
+
+ @Override
+ public boolean isConstString() {
+ return true;
+ }
+
+ @Override
+ public ConstMethodType asConstMethodType() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 24c3925..5fd2ff1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -458,6 +458,14 @@
return null;
}
+ public boolean isConstMethodType() {
+ return false;
+ }
+
+ public ConstMethodType asConstMethodType() {
+ return null;
+ }
+
public boolean isConstString() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index c84adcb..70a9804 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstMethodHandle;
+import com.android.tools.r8.ir.code.ConstMethodType;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.ConstType;
@@ -813,6 +814,19 @@
add(instruction);
}
+ public void addConstMethodType(int dest, DexProto methodType)
+ throws ApiLevelException {
+ if (!options.canUseConstantMethodType()) {
+ throw new ApiLevelException(
+ AndroidApiLevel.P,
+ "Const-method-type",
+ null /* sourceString */);
+ }
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ ConstMethodType instruction = new ConstMethodType(out, methodType);
+ add(instruction);
+ }
+
public void addConstString(int dest, DexString string) {
Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
ConstString instruction = new ConstString(out, string);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index a4203a4..9f3d3a4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -143,6 +143,7 @@
static final Type INT_ARRAY_TYPE = Type.getObjectType(INT_ARRAY_DESC);
static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
static final Type METHOD_HANDLE_TYPE = Type.getObjectType("java/lang/invoke/MethodHandle");
+ static final Type METHOD_TYPE_TYPE = Type.getObjectType("java/lang/invoke/MethodType");
private static final int[] NO_TARGETS = {};
@@ -2730,8 +2731,13 @@
private void build(LdcInsnNode insn, IRBuilder builder) throws ApiLevelException {
if (insn.cst instanceof Type) {
Type type = (Type) insn.cst;
- int dest = state.push(type);
- builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+ if (type.getSort() == Type.METHOD) {
+ int dest = state.push(METHOD_TYPE_TYPE);
+ builder.addConstMethodType(dest, application.getProto(type.getDescriptor()));
+ } else {
+ int dest = state.push(type);
+ builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+ }
} else if (insn.cst instanceof String) {
int dest = state.push(STRING_TYPE);
builder.addConstString(dest, application.getString((String) insn.cst));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index f11af49..6faba19 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -122,7 +122,7 @@
// Catch all matching.
if (other == REFERENCE_TYPE) {
- return sort == Type.OBJECT || sort == Type.ARRAY;
+ return sort == Type.OBJECT || sort == Type.ARRAY || sort == Type.METHOD;
}
if (other == OBJECT_TYPE) {
return sort == Type.OBJECT;
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index e1599b9..46bcbd5 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -48,7 +48,11 @@
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Type) {
- registry.registerConstClass(application.getType((Type) cst));
+ // Nothing to register for method type, it represents only a prototype not associated with a
+ // method name.
+ if (((Type) cst).getSort() != Type.METHOD) {
+ registry.registerConstClass(application.getType((Type) cst));
+ }
} else if (cst instanceof Handle) {
registerMethodHandleType((Handle) cst);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 9f883bf..7f6fe45 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -354,6 +354,10 @@
return minApiLevel >= AndroidApiLevel.P.getLevel();
}
+ public boolean canUseConstantMethodType() {
+ return minApiLevel >= AndroidApiLevel.P.getLevel();
+ }
+
public boolean canUseInvokeCustom() {
return minApiLevel >= AndroidApiLevel.O.getLevel();
}
diff --git a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
index 282b5ae..e8447a3 100644
--- a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
@@ -24,11 +24,15 @@
System.out.println(mhGetStatic.invoke());
}
+ private static void targetMethodTest3(MethodType mt)
+ throws Throwable {
+ System.out.println("MethodType: " + mt.toString());
+ }
+
public static CallSite bsmLookupStatic(MethodHandles.Lookup caller, String name, MethodType type)
throws NoSuchMethodException, IllegalAccessException {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
return new ConstantCallSite(targetMH.asType(type));
}
-
}
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
index b9a5126..375dc34 100644
--- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -43,6 +43,7 @@
@Override
public void visitEnd() {
generateMethodTest1(cw);
+ generateMethodTest2(cw);
generateMethodMain(cw);
super.visitEnd();
}
@@ -56,6 +57,8 @@
Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
@@ -81,4 +84,22 @@
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
+ */
+ private void generateMethodTest2(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(
+ CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn(Type.getMethodType("(ZBSCIFJDLjava/lang/String;)Ljava/lang/Object;"));
+ mv.visitInvokeDynamicInsn("targetMethodTest3", "(Ljava/lang/invoke/MethodType;)V",
+ bootstrap);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 171cbd2..1fb3175 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -976,9 +976,6 @@
);
private static List<String> failuresToTriage = ImmutableList.of(
- // const-method-handle and const-method-type
- "979-const-method-handle",
-
// Dex file input into a jar file, not yet supported by the test framework.
"663-odd-dex-size",
"663-odd-dex-size2",
@@ -1667,6 +1664,7 @@
specification.directory.listFiles((File file) ->
file.getName().endsWith(".dex") && !file.getName().startsWith("jasmin"));
}
+
List<String> fileNames = new ArrayList<>();
for (File file : inputFiles) {
fileNames.add(file.getCanonicalPath());
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d0cc56a..b2f2a58 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -428,6 +428,10 @@
}
public static String getAndroidJar(int minSdkVersion) {
+ if (minSdkVersion == AndroidApiLevel.P.getLevel()) {
+ // TODO(mikaelpeltier) Android P does not yet have his android.jar use the O version
+ minSdkVersion = AndroidApiLevel.O.getLevel();
+ }
return String.format(
ANDROID_JAR_PATTERN,
minSdkVersion == AndroidApiLevel.getDefault().getLevel() ? DEFAULT_MIN_SDK : minSdkVersion);