Improve InvokeDynamic and ConstDynamic modeling
Bug: b/336510513
Change-Id: I7c0eca175e5c83fd5f9c88e168cfd0d94393b28d
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
index c16eae4..9936ef6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -31,9 +31,10 @@
import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
-import java.util.ArrayList;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.List;
import java.util.ListIterator;
+import java.util.function.Supplier;
import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.MethodVisitor;
@@ -41,22 +42,8 @@
private final ConstantDynamicReference reference;
- public CfConstDynamic(
- int symbolicReferenceId,
- DexString name,
- DexType type,
- DexMethodHandle bootstrapMethod,
- List<DexValue> bootstrapMethodArguments) {
- assert symbolicReferenceId >= 0;
- assert name != null;
- assert type != null;
- assert bootstrapMethod != null;
- assert bootstrapMethodArguments != null;
- assert bootstrapMethodArguments.isEmpty();
-
- reference =
- new ConstantDynamicReference(
- symbolicReferenceId, name, type, bootstrapMethod, bootstrapMethodArguments);
+ public CfConstDynamic(ConstantDynamicReference constantDynamicReference) {
+ reference = constantDynamicReference;
}
@Override
@@ -86,27 +73,14 @@
}
public static CfConstDynamic fromAsmConstantDynamic(
- int symbolicReferenceId,
ConstantDynamic insn,
JarApplicationReader application,
- DexType clazz) {
- String constantName = insn.getName();
- String constantDescriptor = insn.getDescriptor();
- DexMethodHandle bootstrapMethodHandle =
- DexMethodHandle.fromAsmHandle(insn.getBootstrapMethod(), application, clazz);
- int argumentCount = insn.getBootstrapMethodArgumentCount();
- List<DexValue> bootstrapMethodArguments = new ArrayList<>(argumentCount);
- for (int i = 0; i < argumentCount; i++) {
- Object argument = insn.getBootstrapMethodArgument(i);
- DexValue dexValue = DexValue.fromAsmBootstrapArgument(argument, application, clazz);
- bootstrapMethodArguments.add(dexValue);
- }
+ DexType clazz,
+ Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
+ assert insn.getBootstrapMethodArgumentCount() == 0;
return new CfConstDynamic(
- symbolicReferenceId,
- application.getString(constantName),
- application.getTypeFromDescriptor(constantDescriptor),
- bootstrapMethodHandle,
- bootstrapMethodArguments);
+ ConstantDynamicReference.fromAsmConstantDynamic(
+ insn, application, clazz, constantDynamicSymbolicReferencesSupplier));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index c09e32a..2cf72f1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.io.BaseEncoding;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
@@ -24,9 +25,10 @@
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
public final class DexCallSite extends IndexedDexItem
implements StructuralItem<DexCallSite>, LirConstant {
@@ -76,17 +78,13 @@
}
public static DexCallSite fromAsmInvokeDynamic(
- InvokeDynamicInsnNode insn, JarApplicationReader application, DexType clazz) {
- return fromAsmInvokeDynamic(application, clazz, insn.name, insn.desc, insn.bsm, insn.bsmArgs);
- }
-
- public static DexCallSite fromAsmInvokeDynamic(
JarApplicationReader application,
DexType clazz,
String name,
String desc,
Handle bsmHandle,
- Object[] bsmArgs) {
+ Object[] bsmArgs,
+ Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
// Bootstrap method
if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC
&& bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
@@ -99,7 +97,9 @@
// Decode static bootstrap arguments
List<DexValue> bootstrapArgs = new ArrayList<>();
for (Object arg : bsmArgs) {
- bootstrapArgs.add(DexValue.fromAsmBootstrapArgument(arg, application, clazz));
+ bootstrapArgs.add(
+ DexValue.fromAsmBootstrapArgument(
+ arg, application, clazz, constantDynamicSymbolicReferencesSupplier));
}
// Construct call site
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 38b5634..f4f7a60 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.FileWriter;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -19,6 +20,7 @@
import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicReference;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.EncodedValueUtils;
@@ -26,8 +28,11 @@
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.Arrays;
import java.util.function.Consumer;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;
@@ -51,7 +56,8 @@
ARRAY(0x1c),
ANNOTATION(0x1d),
NULL(0x1e),
- BOOLEAN(0x1f);
+ BOOLEAN(0x1f),
+ CONST_DYNAMIC(-1);
public static DexValueKind fromId(int id) {
switch (id) {
@@ -91,6 +97,8 @@
return NULL;
case 0x1f:
return BOOLEAN;
+ case -1:
+ return CONST_DYNAMIC;
default:
throw new Unreachable();
}
@@ -162,6 +170,14 @@
return null;
}
+ public boolean isDexValueConstDynamic() {
+ return false;
+ }
+
+ public DexValueConstDynamic asDexValueConstDynamic() {
+ return null;
+ }
+
public boolean isDexValueMethodType() {
return false;
}
@@ -313,7 +329,10 @@
public abstract AbstractValue toAbstractValue(AbstractValueFactory factory);
public static DexValue fromAsmBootstrapArgument(
- Object value, JarApplicationReader application, DexType clazz) {
+ Object value,
+ JarApplicationReader application,
+ DexType clazz,
+ Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
if (value instanceof Integer) {
return DexValue.DexValueInt.create((Integer) value);
} else if (value instanceof Long) {
@@ -334,12 +353,23 @@
case Type.METHOD:
return new DexValue.DexValueMethodType(
application.getProto(((Type) value).getDescriptor()));
+ case Type.ARRAY:
+ DexType arrayType = application.getTypeFromDescriptor(((Type) value).getDescriptor());
+ assert arrayType.isArrayType();
+ return new DexValue.DexValueType(arrayType);
default:
throw new Unreachable("Type sort is not supported: " + type.getSort());
}
} else if (value instanceof Handle) {
return new DexValue.DexValueMethodHandle(
DexMethodHandle.fromAsmHandle((Handle) value, application, clazz));
+ } else if (value instanceof ConstantDynamic) {
+ return new DexValue.DexValueConstDynamic(
+ ConstantDynamicReference.fromAsmConstantDynamic(
+ (ConstantDynamic) value,
+ application,
+ clazz,
+ constantDynamicSymbolicReferencesSupplier));
} else {
throw new Unreachable(
"Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
@@ -2054,4 +2084,96 @@
return UnknownValue.getInstance();
}
}
+
+ public static class DexValueConstDynamic extends DexValue {
+
+ private final ConstantDynamicReference value;
+
+ public DexValueConstDynamic(ConstantDynamicReference value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean isDexValueConstDynamic() {
+ return true;
+ }
+
+ @Override
+ public DexValueConstDynamic asDexValueConstDynamic() {
+ return this;
+ }
+
+ @Override
+ int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ return value.acceptCompareTo(other.asDexValueConstDynamic().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
+ public DexValueKind getValueKind() {
+ return DexValueKind.CONST_DYNAMIC;
+ }
+
+ private CompilationError throwCannotConvertToDex() {
+ throw new CompilationError("DexValueConstDynamic should be desugared");
+ }
+
+ @Override
+ public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
+ throw throwCannotConvertToDex();
+ }
+
+ @Override
+ public void sort() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ throw throwCannotConvertToDex();
+ }
+
+ @Override
+ public int hashCode() {
+ return 7 * value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof DexValueConstDynamic) {
+ DexValueConstDynamic otherCstDynamic = (DexValueConstDynamic) other;
+ return otherCstDynamic.value.equals(value);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Item " + getValueKind() + " " + value;
+ }
+
+ @Override
+ public DexType getType(DexItemFactory factory) {
+ return null;
+ }
+
+ @Override
+ public Object getBoxedValue() {
+ throw new Unreachable("No boxed value for DexValueConstDynamic");
+ }
+
+ @Override
+ public Object asAsmEncodedObject() {
+ throw new Unreachable("No ASM conversion for DexValueConstDynamic");
+ }
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 2ad9817..c41f8df 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -921,7 +921,14 @@
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
DexCallSite callSite =
- DexCallSite.fromAsmInvokeDynamic(application, method.holder, name, desc, bsm, bsmArgs);
+ DexCallSite.fromAsmInvokeDynamic(
+ application,
+ method.holder,
+ name,
+ desc,
+ bsm,
+ bsmArgs,
+ constantDynamicSymbolicReferencesSupplier);
addInstruction(new CfInvokeDynamic(callSite));
}
@@ -1027,16 +1034,12 @@
// instance from ASM, even when they are equal (i.e. all their components are equal). See
// ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest
// for an example.
- Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences =
- constantDynamicSymbolicReferencesSupplier.get();
- int symbolicReferenceId = constantDynamicSymbolicReferences.getOrDefault(cst, -1);
- if (symbolicReferenceId == -1) {
- symbolicReferenceId = constantDynamicSymbolicReferences.size();
- constantDynamicSymbolicReferences.put((ConstantDynamic) cst, symbolicReferenceId);
- }
addInstruction(
CfConstDynamic.fromAsmConstantDynamic(
- symbolicReferenceId, (ConstantDynamic) cst, application, method.holder));
+ (ConstantDynamic) cst,
+ application,
+ method.holder,
+ constantDynamicSymbolicReferencesSupplier));
} else {
throw new CompilationError("Unsupported constant: " + cst.toString());
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
index 478e4cf..2f27eab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
@@ -7,12 +7,17 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.utils.structural.Equatable;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
public class ConstantDynamicReference implements StructuralItem<ConstantDynamicReference> {
private final int symbolicReferenceId;
@@ -25,13 +30,53 @@
spec.withInt(c -> c.symbolicReferenceId);
}
- public ConstantDynamicReference(
+ public static ConstantDynamicReference fromAsmConstantDynamic(
+ ConstantDynamic cst,
+ JarApplicationReader application,
+ DexType clazz,
+ Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
+
+ Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences =
+ constantDynamicSymbolicReferencesSupplier.get();
+ int symbolicReferenceId = constantDynamicSymbolicReferences.getOrDefault(cst, -1);
+ if (symbolicReferenceId == -1) {
+ symbolicReferenceId = constantDynamicSymbolicReferences.size();
+ constantDynamicSymbolicReferences.put(cst, symbolicReferenceId);
+ }
+
+ String constantName = cst.getName();
+ String constantDescriptor = cst.getDescriptor();
+ DexMethodHandle bootstrapMethodHandle =
+ DexMethodHandle.fromAsmHandle(cst.getBootstrapMethod(), application, clazz);
+ int argumentCount = cst.getBootstrapMethodArgumentCount();
+ List<DexValue> bootstrapMethodArguments1 = new ArrayList<>(argumentCount);
+ for (int i = 0; i < argumentCount; i++) {
+ Object argument = cst.getBootstrapMethodArgument(i);
+ DexValue dexValue =
+ DexValue.fromAsmBootstrapArgument(
+ argument, application, clazz, constantDynamicSymbolicReferencesSupplier);
+ bootstrapMethodArguments1.add(dexValue);
+ }
+
+ return new ConstantDynamicReference(
+ symbolicReferenceId,
+ application.getString(constantName),
+ application.getTypeFromDescriptor(constantDescriptor),
+ bootstrapMethodHandle,
+ bootstrapMethodArguments1);
+ }
+
+ private ConstantDynamicReference(
int symbolicReferenceId,
DexString name,
DexType type,
DexMethodHandle bootstrapMethod,
List<DexValue> bootstrapMethodArguments) {
- assert bootstrapMethodArguments.isEmpty();
+ assert symbolicReferenceId >= 0;
+ assert name != null;
+ assert type != null;
+ assert bootstrapMethod != null;
+ assert bootstrapMethodArguments != null;
this.symbolicReferenceId = symbolicReferenceId;
this.name = name;
this.type = type;
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
index 07f10ad..90001a5 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -41,38 +40,31 @@
CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
// javac generated an invokedynamic using bootstrap method
// java.lang.runtime.SwitchBootstraps.typeSwitch.
- try {
- assertEquals(
- 1,
- inspector
- .clazz(Main.class)
- .uniqueMethodWithOriginalName("enumSwitch")
- .streamInstructions()
- .filter(InstructionSubject::isInvokeDynamic)
- .map(
- instruction ->
- instruction
- .asCfInstruction()
- .getInstruction()
- .asInvokeDynamic()
- .getCallSite()
- .getBootstrapMethod()
- .member
- .asDexMethod())
- .filter(
- method ->
- method
- .getHolderType()
- .toString()
- .contains("java.lang.runtime.SwitchBootstraps"))
- .filter(method -> method.toString().contains("typeSwitch"))
- .count());
- } catch (CompilationError e) {
- assertEquals("Could not parse code", e.getMessage());
- assertEquals(
- "Unsupported bootstrap static argument of type ConstantDynamic",
- e.getCause().getMessage());
- }
+ assertEquals(
+ 1,
+ inspector
+ .clazz(Main.class)
+ .uniqueMethodWithOriginalName("enumSwitch")
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeDynamic)
+ .map(
+ instruction ->
+ instruction
+ .asCfInstruction()
+ .getInstruction()
+ .asInvokeDynamic()
+ .getCallSite()
+ .getBootstrapMethod()
+ .member
+ .asDexMethod())
+ .filter(
+ method ->
+ method
+ .getHolderType()
+ .toString()
+ .contains("java.lang.runtime.SwitchBootstraps"))
+ .filter(method -> method.toString().contains("typeSwitch"))
+ .count());
parameters.assumeJvmTestParameters();
testForJvm(parameters)
@@ -116,6 +108,7 @@
static final class C implements I {}
static class Main {
+
static void enumSwitch(I i) {
switch (i) {
case E.E1 -> {
diff --git a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
index a330d75..144af9d 100644
--- a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
@@ -104,18 +104,18 @@
case null -> {
System.out.println("null");
}
- case "y", "Y" -> {
- System.out.println("y or Y");
- }
- case "n", "N" -> {
- System.out.println("n or N");
- }
case String s when s.equalsIgnoreCase("YES") -> {
System.out.println("yes");
}
+ case "y", "Y" -> {
+ System.out.println("y or Y");
+ }
case String s when s.equalsIgnoreCase("NO") -> {
System.out.println("no");
}
+ case "n", "N" -> {
+ System.out.println("n or N");
+ }
case String s -> {
System.out.println("unknown");
}
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
index 5ad94ba..f272cde 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -48,24 +47,19 @@
CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
// javac generated an invokedynamic using bootstrap method argument of an arrya type (sort 9
// is org.objectweb.asm.Type.ARRAY).
- try {
inspector
.clazz(Main.class)
.uniqueMethodWithOriginalName("typeSwitch")
.streamInstructions()
.filter(InstructionSubject::isInvokeDynamic)
.count();
- } catch (CompilationError e) {
- assertEquals("Could not parse code", e.getMessage());
- assertEquals("Type sort is not supported: 9", e.getCause().getMessage());
- }
// javac generated an invokedynamic using bootstrap method
// java.lang.runtime.SwitchBootstraps.typeSwitch.
assertEquals(
1,
inspector
.clazz(Main.class)
- .uniqueMethodWithOriginalName("typeSwitchNoArray")
+ .uniqueMethodWithOriginalName("typeSwitch")
.streamInstructions()
.filter(InstructionSubject::isInvokeDynamic)
.map(
@@ -127,17 +121,6 @@
static class Main {
- // No array version only for loading into code inspector.
- static void typeSwitchNoArray(Object obj) {
- switch (obj) {
- case null -> System.out.println("null");
- case String string -> System.out.println("String");
- case Color color -> System.out.println("Color: " + color);
- case Point point -> System.out.println("Record class: " + point);
- default -> System.out.println("Other");
- }
- }
-
static void typeSwitch(Object obj) {
switch (obj) {
case null -> System.out.println("null");