Merge "Add non-null IR _after_ inlining."
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6f062cf..5a0d2c4 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.2.4-dev";
+ public static final String LABEL = "v1.2.6-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 39d916a..181ebac 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -16,6 +16,9 @@
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Uninitialized;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedNew;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedThis;
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfIfCmp;
@@ -240,7 +243,17 @@
StringBuilder builder = new StringBuilder("frame: [");
String separator = "";
for (Entry<DexType> entry : frame.getLocals().int2ReferenceEntrySet()) {
- builder.append(separator).append(entry.getIntKey()).append(':').append(entry.getValue());
+ builder.append(separator).append(entry.getIntKey()).append(':');
+ Uninitialized allocator = frame.getAllocators().get(entry.getIntKey());
+ if (allocator == null) {
+ builder.append(entry.getValue());
+ } else if (allocator instanceof UninitializedThis) {
+ builder.append("uninitialized this");
+ } else {
+ builder
+ .append("uninitialized ")
+ .append(getLabel(((UninitializedNew) allocator).getLabel()));
+ }
separator = ", ";
}
builder.append("] ");
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 2818e44..b5e759c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -17,11 +17,44 @@
public class CfFrame extends CfInstruction {
+ public abstract static class Uninitialized {
+ abstract Object getAsmLabel();
+ }
+
+ public static class UninitializedNew extends Uninitialized {
+ private final CfLabel label;
+
+ public UninitializedNew(CfLabel label) {
+ this.label = label;
+ }
+
+ @Override
+ Object getAsmLabel() {
+ return label.getLabel();
+ }
+
+ public CfLabel getLabel() {
+ return label;
+ }
+ }
+
+ public static class UninitializedThis extends Uninitialized {
+ @Override
+ Object getAsmLabel() {
+ return Opcodes.UNINITIALIZED_THIS;
+ }
+ }
+
private final Int2ReferenceSortedMap<DexType> locals;
+ private final Int2ReferenceSortedMap<Uninitialized> allocators;
private final List<DexType> stack;
- public CfFrame(Int2ReferenceSortedMap<DexType> locals, List<DexType> stack) {
+ public CfFrame(
+ Int2ReferenceSortedMap<DexType> locals,
+ Int2ReferenceSortedMap<Uninitialized> allocators,
+ List<DexType> stack) {
this.locals = locals;
+ this.allocators = allocators;
this.stack = stack;
}
@@ -29,6 +62,10 @@
return locals;
}
+ public Int2ReferenceSortedMap<Uninitialized> getAllocators() {
+ return allocators;
+ }
+
public List<DexType> getStack() {
return stack;
}
@@ -88,7 +125,8 @@
int localIndex = 0;
for (int i = 0; i <= maxRegister; i++) {
DexType type = locals.get(i);
- Object typeOpcode = getType(type, lens);
+ Uninitialized allocator = allocators.get(i);
+ Object typeOpcode = allocator == null ? getType(type, lens) : allocator.getAsmLabel();
localsTypes[localIndex++] = typeOpcode;
if (type != null && isWide(type)) {
i++;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index a681943..f830427 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -46,8 +46,8 @@
DexString methodName;
if (lens.isIdentityLens()) {
methodName = callSite.methodName;
- } else if (callSite.interfaceMethod != null) {
- methodName = lens.lookupName(callSite.interfaceMethod);
+ } else if (!callSite.interfaceMethods.isEmpty()) {
+ methodName = lens.lookupName(callSite.interfaceMethods.get(0));
} else {
throw new Unimplemented("Minification of non-lambda InvokeDynamic not supported");
}
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index 45a62ef..e3753a7 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -27,40 +28,80 @@
private static final boolean PRINT_ARGS = false;
public static class Options {
- List<String> inputArchives = new ArrayList<>();
- List<String> featureJars = new ArrayList<>();
- String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
- String featureSplitMapping;
- String proguardMap;
+ private List<String> inputArchives = new ArrayList<>();
+ private List<String> featureJars = new ArrayList<>();
+ private String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
+ private String featureSplitMapping;
+ private String proguardMap;
+
+ public String getSplitBaseName() {
+ return splitBaseName;
+ }
+
+ public void setSplitBaseName(String splitBaseName) {
+ this.splitBaseName = splitBaseName;
+ }
+
+ public String getFeatureSplitMapping() {
+ return featureSplitMapping;
+ }
+
+ public void setFeatureSplitMapping(String featureSplitMapping) {
+ this.featureSplitMapping = featureSplitMapping;
+ }
+
+ public String getProguardMap() {
+ return proguardMap;
+ }
+
+ public void setProguardMap(String proguardMap) {
+ this.proguardMap = proguardMap;
+ }
+
+ public void addInputArchive(String inputArchive) {
+ inputArchives.add(inputArchive);
+ }
+
+ public void addFeatureJar(String featureJar) {
+ featureJars.add(featureJar);
+ }
+
+ public ImmutableList<String> getInputArchives() {
+ return ImmutableList.copyOf(inputArchives);
+ }
+
+ public ImmutableList<String> getFeatureJars() {
+ return ImmutableList.copyOf(featureJars);
+ }
}
private static Options parseArguments(String[] args) throws IOException {
Options options = new Options();
ParseContext context = new ParseContext(args);
while (context.head() != null) {
- List<String> input = OptionsParsing.tryParseMulti(context, "--input");
- if (input != null) {
- options.inputArchives.addAll(input);
+ List<String> inputs = OptionsParsing.tryParseMulti(context, "--input");
+ if (inputs != null) {
+ inputs.stream().forEach(options::addInputArchive);
continue;
}
List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar");
if (featureJars != null) {
- options.featureJars.addAll(featureJars);
+ featureJars.stream().forEach(options::addFeatureJar);
continue;
}
String output = OptionsParsing.tryParseSingle(context, "--output", "-o");
if (output != null) {
- options.splitBaseName = output;
+ options.setSplitBaseName(output);
continue;
}
String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null);
if (proguardMap != null) {
- options.proguardMap = proguardMap;
+ options.setProguardMap(proguardMap);
continue;
}
String featureSplit = OptionsParsing.tryParseSingle(context, "--feature-splits", null);
if (featureSplit != null) {
- options.featureSplitMapping = featureSplit;
+ options.setFeatureSplitMapping(featureSplit);
continue;
}
throw new RuntimeException(String.format("Unknown options: '%s'.", context.head()));
@@ -70,8 +111,8 @@
private static FeatureClassMapping createFeatureClassMapping(Options options)
throws IOException, FeatureMappingException, ResourceException {
- if (options.featureSplitMapping != null) {
- return FeatureClassMapping.fromSpecification(Paths.get(options.featureSplitMapping));
+ if (options.getFeatureSplitMapping() != null) {
+ return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
}
assert !options.featureJars.isEmpty();
return FeatureClassMapping.fromJarFiles(options.featureJars);
@@ -87,13 +128,13 @@
public static void run(Options options)
throws IOException, FeatureMappingException, ResourceException, CompilationException,
ExecutionException, CompilationFailedException {
- if (options.inputArchives.isEmpty()) {
+ if (options.getInputArchives().isEmpty()) {
throw new RuntimeException("Need at least one --input");
}
- if (options.featureSplitMapping == null && options.featureJars.isEmpty()) {
+ if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
throw new RuntimeException("You must supply a feature split mapping or feature jars");
}
- if (options.featureSplitMapping != null && !options.featureJars.isEmpty()) {
+ if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
}
@@ -108,7 +149,7 @@
FeatureClassMapping featureClassMapping = createFeatureClassMapping(options);
DexSplitterHelper.run(
- builder.build(), featureClassMapping, options.splitBaseName, options.proguardMap);
+ builder.build(), featureClassMapping, options.getSplitBaseName(), options.getProguardMap());
}
public static void main(String[] args) {
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 9e22713..ac954f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -29,22 +29,27 @@
public final DexMethodHandle bootstrapMethod;
public final List<DexValue> bootstrapArgs;
- public final DexMethod interfaceMethod;
+ public final List<DexMethod> interfaceMethods;
private DexEncodedArray encodedArray = null;
- DexCallSite(DexString methodName, DexProto methodProto,
- DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs, DexMethod interfaceMethod) {
+ DexCallSite(
+ DexString methodName,
+ DexProto methodProto,
+ DexMethodHandle bootstrapMethod,
+ List<DexValue> bootstrapArgs,
+ List<DexMethod> interfaceMethods) {
assert methodName != null;
assert methodProto != null;
assert bootstrapMethod != null;
assert bootstrapArgs != null;
+ assert interfaceMethods != null;
this.methodName = methodName;
this.methodProto = methodProto;
this.bootstrapMethod = bootstrapMethod;
this.bootstrapArgs = bootstrapArgs;
- this.interfaceMethod = interfaceMethod;
+ this.interfaceMethods = interfaceMethods;
}
public static DexCallSite fromAsmInvokeDynamic(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 75f2ee8..5fe9134 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.naming.NamingLens;
import com.google.common.base.Strings;
@@ -26,6 +27,7 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -228,6 +230,33 @@
public final DexType annotationSynthesizedClassMap =
createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
+ private static final String METAFACTORY_METHOD_NAME = "metafactory";
+ private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+ public final DexType metafactoryType = createType("Ljava/lang/invoke/LambdaMetafactory;");
+ public final DexType callSiteType = createType("Ljava/lang/invoke/CallSite;");
+ public final DexType lookupType = createType("Ljava/lang/invoke/MethodHandles$Lookup;");
+ public final DexType serializableType = createType("Ljava/io/Serializable;");
+
+ public final DexMethod metafactoryMethod =
+ createMethod(
+ metafactoryType,
+ createProto(
+ callSiteType,
+ lookupType,
+ stringType,
+ methodTypeType,
+ methodTypeType,
+ methodHandleType,
+ methodTypeType),
+ createString(METAFACTORY_METHOD_NAME));
+
+ public final DexMethod metafactoryAltMethod =
+ createMethod(
+ metafactoryType,
+ createProto(callSiteType, lookupType, stringType, methodTypeType, objectArrayType),
+ createString(METAFACTORY_ALT_METHOD_NAME));
+
private boolean skipNameValidationForTesting = false;
public void setSkipNameValidationForTesting(boolean skipNameValidationForTesting) {
@@ -545,23 +574,46 @@
DexString methodName, DexProto methodProto,
DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
assert !sorted;
- String bootstrapClass = bootstrapMethod.asMethod().holder.toDescriptorString();
- DexMethod interfaceMethod = null;
- if (bootstrapClass.equals("Ljava/lang/invoke/LambdaMetafactory;")) {
- if (methodName.toString().equals("metafactory")) {
- DexType interfaceType = methodProto.returnType;
- assert bootstrapArgs.size() == 3;
- // bootstrapArgs contains samMethodType, implMethod and instantiatedMethodType.
- DexValueMethodType samMethodType = (DexValueMethodType) bootstrapArgs.get(0);
- interfaceMethod = createMethod(interfaceType, samMethodType.value, methodName);
- }
- // TODO(mathiasr): Support altMetafactory, possibly using ir.desugar.LambdaDescriptor
- }
+ List<DexMethod> interfaceMethods =
+ getCallSiteInterfaceMethods(methodName, methodProto, bootstrapMethod, bootstrapArgs);
DexCallSite callSite =
- new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethod);
+ new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethods);
return canonicalize(callSites, callSite);
}
+ private List<DexMethod> getCallSiteInterfaceMethods(
+ DexString methodName,
+ DexProto methodProto,
+ DexMethodHandle bootstrapMethodHandle,
+ List<DexValue> bootstrapArgs) {
+ // TODO(mathiasr): Unify this with LambdaDescriptor.infer().
+ if (!bootstrapMethodHandle.type.isInvokeStatic()) {
+ return Collections.emptyList();
+ }
+ DexMethod bootstrapMethod = bootstrapMethodHandle.asMethod();
+ if (bootstrapMethod != metafactoryMethod && bootstrapMethod != metafactoryAltMethod) {
+ return Collections.emptyList();
+ }
+ DexType interfaceType = methodProto.returnType;
+ assert bootstrapMethod == metafactoryAltMethod || bootstrapArgs.size() == 3;
+ // Signature of main functional interface method.
+ // In Java docs, this argument is named 'samMethodType'.
+ DexValueMethodType funcErasedSignature = (DexValueMethodType) bootstrapArgs.get(0);
+ DexMethod mainMethod = createMethod(interfaceType, funcErasedSignature.value, methodName);
+ if (bootstrapMethod == metafactoryAltMethod) {
+ List<DexMethod> result = new ArrayList<>();
+ result.add(mainMethod);
+ LambdaDescriptor.extractAltMetafactory(
+ this,
+ bootstrapArgs,
+ type -> result.add(createMethod(type, funcErasedSignature.value, methodName)),
+ bridge -> {});
+ return result;
+ } else {
+ return Collections.singletonList(mainMethod);
+ }
+ }
+
public DexMethod createMethod(DexString clazzDescriptor, DexString name,
DexString returnTypeDescriptor,
DexString[] parameterDescriptors) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 7c04392..8dd9162 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -7,6 +7,9 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Uninitialized;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedNew;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedThis;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfPosition;
@@ -28,8 +31,10 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.JumpInstruction;
import com.android.tools.r8.ir.code.Load;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.StackValue;
import com.android.tools.r8.ir.code.Store;
@@ -42,9 +47,11 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -77,6 +84,10 @@
private AppInfoWithSubtyping appInfo;
+ private Map<NewInstance, List<InvokeDirect>> initializers;
+ private List<InvokeDirect> thisInitializers;
+ private Map<NewInstance, CfLabel> newInstanceLabels;
+
// Internal abstraction of the stack values and height.
private static class Stack {
int maxHeight = 0;
@@ -110,6 +121,7 @@
public CfCode build(
CodeRewriter rewriter, InternalOptions options, AppInfoWithSubtyping appInfo) {
+ computeInitializers();
types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
splitExceptionalBlocks();
LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, types);
@@ -133,6 +145,39 @@
return resolvedField == null ? field : resolvedField.field;
}
+ private void computeInitializers() {
+ assert initializers == null;
+ assert thisInitializers == null;
+ initializers = new HashMap<>();
+ for (BasicBlock block : code.blocks) {
+ for (Instruction insn : block.getInstructions()) {
+ if (insn.isNewInstance()) {
+ initializers.put(insn.asNewInstance(), computeInitializers(insn.outValue()));
+ } else if (insn.isArgument() && method.isInstanceInitializer()) {
+ if (insn.outValue().isThis()) {
+ // By JVM8 §4.10.1.9 (invokespecial), a this() or super() call in a constructor
+ // changes the type of `this` from uninitializedThis
+ // to the type of the class of the <init> method.
+ thisInitializers = computeInitializers(insn.outValue());
+ }
+ }
+ }
+ }
+ assert !(method.isInstanceInitializer() && thisInitializers == null);
+ }
+
+ private List<InvokeDirect> computeInitializers(Value value) {
+ List<InvokeDirect> initializers = new ArrayList<>();
+ for (Instruction user : value.uniqueUsers()) {
+ if (user instanceof InvokeDirect
+ && user.inValues().get(0) == value
+ && user.asInvokeDirect().getInvokedMethod().name == factory.constructorMethodName) {
+ initializers.add(user.asInvokeDirect());
+ }
+ }
+ return initializers;
+ }
+
// Split all blocks with throwing instructions and exceptional edges such that any non-throwing
// instructions that might define values prior to the throwing exception are excluded from the
// try-catch range. Failure to do so will result in code that does not verify on the JVM.
@@ -203,6 +248,7 @@
List<CfTryCatch> tryCatchRanges = new ArrayList<>();
labels = new HashMap<>(code.blocks.size());
emittedLabels = new HashSet<>(code.blocks.size());
+ newInstanceLabels = new HashMap<>(initializers.size());
instructions = new ArrayList<>();
ListIterator<BasicBlock> blockIterator = code.listIterator();
BasicBlock block = blockIterator.next();
@@ -211,6 +257,7 @@
BasicBlock pendingFrame = null;
boolean previousFallthrough = false;
do {
+ assert stack.isEmpty();
CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
if (!tryCatchHandlers.equals(handlers)) {
if (!tryCatchHandlers.isEmpty()) {
@@ -230,7 +277,6 @@
// If previousBlock is fallthrough, then it is counted in getPredecessors().size(), but
// we only want to set a pendingFrame if we have a predecessor which is not previousBlock.
if (block.getPredecessors().size() > (previousFallthrough ? 1 : 0)) {
- assert stack.isEmpty();
pendingFrame = block;
emitLabel(getLabel(block));
}
@@ -318,6 +364,9 @@
pendingLocalChanges = true;
}
} else {
+ if (instruction.isNewInstance()) {
+ newInstanceLabels.put(instruction.asNewInstance(), ensureLabel());
+ }
updatePositionAndLocals(instruction);
instruction.buildCf(this);
}
@@ -400,9 +449,11 @@
Collection<Value> locals = registerAllocator.getLocalsAtBlockEntry(block);
Int2ReferenceSortedMap<DexType> mapping = new Int2ReferenceAVLTreeMap<>();
+ Int2ReferenceSortedMap<Uninitialized> allocators = new Int2ReferenceAVLTreeMap<>();
for (Value local : locals) {
DexType type;
+ Uninitialized allocator = null;
switch (local.outType()) {
case INT:
type = factory.intType;
@@ -418,14 +469,64 @@
break;
case OBJECT:
type = types.get(local);
+ allocator = findAllocator(block, local);
break;
default:
throw new Unreachable(
"Unexpected local type: " + local.outType() + " for local: " + local);
}
mapping.put(getLocalRegister(local), type);
+ if (allocator != null) {
+ allocators.put(getLocalRegister(local), allocator);
+ }
}
- instructions.add(new CfFrame(mapping, stackTypes));
+ instructions.add(new CfFrame(mapping, allocators, stackTypes));
+ }
+
+ private Uninitialized findAllocator(BasicBlock liveBlock, Value value) {
+ Instruction definition = value.definition;
+ while (definition != null && (definition.isStore() || definition.isLoad())) {
+ definition = definition.inValues().get(0).definition;
+ }
+ if (definition == null) {
+ return null;
+ }
+ Uninitialized res;
+ if (definition.isNewInstance()) {
+ res = new UninitializedNew(newInstanceLabels.get(definition.asNewInstance()));
+ } else if (definition.isArgument()
+ && method.isInstanceInitializer()
+ && definition.outValue().isThis()) {
+ res = new UninitializedThis();
+ } else {
+ return null;
+ }
+ BasicBlock definitionBlock = definition.getBlock();
+ Set<BasicBlock> visited = new HashSet<>();
+ Deque<BasicBlock> toVisit = new ArrayDeque<>();
+ List<InvokeDirect> valueInitializers =
+ definition.isArgument() ? thisInitializers : initializers.get(definition.asNewInstance());
+ for (InvokeDirect initializer : valueInitializers) {
+ BasicBlock initializerBlock = initializer.getBlock();
+ if (initializerBlock == liveBlock) {
+ return res;
+ }
+ if (initializerBlock != definitionBlock && visited.add(initializerBlock)) {
+ toVisit.addLast(initializerBlock);
+ }
+ }
+ while (!toVisit.isEmpty()) {
+ BasicBlock block = toVisit.removeLast();
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ if (predecessor == liveBlock) {
+ return res;
+ }
+ if (predecessor != definitionBlock && visited.add(predecessor)) {
+ toVisit.addLast(predecessor);
+ }
+ }
+ }
+ return null;
}
private void emitLabel(CfLabel label) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 5d52523..49a797a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -21,9 +21,10 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
// Represents the lambda descriptor inferred from calls site.
-final class LambdaDescriptor {
+public final class LambdaDescriptor {
private static final int LAMBDA_ALT_SERIALIZABLE = 1;
private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
@@ -227,8 +228,8 @@
}
DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
- boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
- boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+ boolean isMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryMethod;
+ boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryAltMethod;
if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
// It is not a lambda, thus no need to manage this call site.
return LambdaDescriptor.MATCH_FAILED;
@@ -240,18 +241,18 @@
// Signature of main functional interface method.
DexValue.DexValueMethodType funcErasedSignature =
- getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+ getBootstrapArgument(callSite.bootstrapArgs, 0, DexValue.DexValueMethodType.class);
// Method handle of the implementation method.
DexMethodHandle lambdaImplMethodHandle =
- getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+ getBootstrapArgument(callSite.bootstrapArgs, 1, DexValue.DexValueMethodHandle.class).value;
// Even though there are some limitations on which method handle kinds are
// allowed for lambda impl-methods, there is no way to detect unsupported
// handle kinds after they are transformed into DEX method handle.
// Signature to be enforced on main method.
DexValue.DexValueMethodType funcEnforcedSignature =
- getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+ getBootstrapArgument(callSite.bootstrapArgs, 2, DexValue.DexValueMethodType.class);
if (!isEnforcedSignatureValid(
rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
throw new Unreachable(
@@ -277,67 +278,70 @@
"Unexpected number of metafactory method arguments in " + callSite.toString());
}
} else {
- extractExtraLambdaInfo(rewriter, callSite, match);
+ extractAltMetafactory(
+ rewriter.factory,
+ callSite.bootstrapArgs,
+ interfaceType -> {
+ if (!match.interfaces.contains(interfaceType)) {
+ match.interfaces.add(interfaceType);
+ }
+ },
+ match.bridges::add);
}
return match;
}
- private static void extractExtraLambdaInfo(
- LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+ public static void extractAltMetafactory(
+ DexItemFactory dexItemFactory,
+ List<DexValue> bootstrapArgs,
+ Consumer<DexType> interfaceConsumer,
+ Consumer<DexProto> bridgeConsumer) {
int argIndex = 3;
- int flagsArg = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int flagsArg =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
// Load extra interfaces if any.
if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
- int count = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
for (int i = 0; i < count; i++) {
- DexType type = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueType.class).value;
- if (!match.interfaces.contains(type)) {
- match.interfaces.add(type);
- }
+ DexType interfaceType =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueType.class).value;
+ interfaceConsumer.accept(interfaceType);
}
}
// If the lambda is serializable, add it.
if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
- if (!match.interfaces.contains(rewriter.serializableType)) {
- match.interfaces.add(rewriter.serializableType);
- }
+ interfaceConsumer.accept(dexItemFactory.serializableType);
}
// Load bridges if any.
if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
- int count = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
for (int i = 0; i < count; i++) {
- DexProto bridgeProto = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueMethodType.class).value;
- match.bridges.add(bridgeProto);
+ DexProto bridgeProto =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueMethodType.class)
+ .value;
+ bridgeConsumer.accept(bridgeProto);
}
}
- if (callSite.bootstrapArgs.size() != argIndex) {
- throw new Unreachable(
- "Unexpected number of metafactory method arguments in " + callSite.toString());
+ if (bootstrapArgs.size() != argIndex) {
+ throw new Unreachable("Unexpected number of metafactory method arguments in DexCallSite");
}
}
@SuppressWarnings("unchecked")
- private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
- List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+ private static <T> T getBootstrapArgument(List<DexValue> bootstrapArgs, int i, Class<T> clazz) {
if (bootstrapArgs.size() < i) {
- throw new Unreachable("Expected to find at least "
- + i + " bootstrap arguments in " + callSite.toString());
+ throw new Unreachable(
+ "Expected to find at least " + i + " bootstrap arguments in DexCallSite");
}
DexValue value = bootstrapArgs.get(i);
if (!clazz.isAssignableFrom(value.getClass())) {
- throw new Unreachable("Unexpected type of "
- + "bootstrap arguments #" + i + " in " + callSite.toString());
+ throw new Unreachable("Unexpected type of bootstrap arguments #" + i + " in DexCallSite");
}
return (T) value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index aad7961..82792df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -41,16 +41,8 @@
* lambda class generation, and instruction patching.
*/
public class LambdaRewriter {
- private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
- private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
- private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
- private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
- private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
- private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
- private static final String METAFACTORY_METHOD_NAME = "metafactory";
- private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
// Public for testing.
@@ -62,12 +54,8 @@
final AppInfo appInfo;
final DexItemFactory factory;
- final DexMethod metafactoryMethod;
final DexMethod objectInitMethod;
- final DexMethod metafactoryAltMethod;
- final DexType serializableType;
-
final DexString constructorName;
final DexString classConstructorName;
final DexString instanceFieldName;
@@ -97,28 +85,11 @@
this.factory = converter.appInfo.dexItemFactory;
this.appInfo = converter.appInfo;
- DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
- DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
- DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
- DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
- DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
-
- this.metafactoryMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
- methodTypeType, methodHandleType, methodTypeType),
- factory.createString(METAFACTORY_METHOD_NAME));
-
- this.metafactoryAltMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
- factory.objectArrayType),
- factory.createString(METAFACTORY_ALT_METHOD_NAME));
-
this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
DexProto initProto = factory.createProto(factory.voidType);
this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
- this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
this.deserializeLambdaMethodProto = factory.createProto(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 7eb771c..678ddae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -205,7 +205,7 @@
}
return NEVER;
} else {
- /* package-private */
+ /* package-private */
return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index ce37763..25a84b6 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence;
@@ -88,13 +87,10 @@
*/
class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
- private final Equivalence<DexMethod> equivalence;
+ private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
super(appInfo, rootSet, options);
- equivalence = overloadAggressively
- ? MethodSignatureEquivalence.get()
- : MethodJavaSignatureEquivalence.get();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index de171a3..3372a20 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -11,8 +11,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
+import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -124,7 +125,7 @@
// Rebind to the lowest library class or program class.
if (target != null && target.method != method) {
DexClass targetClass = appInfo.definitionFor(target.method.holder);
- // If the targetclass is not public but the targeted method is, we might run into
+ // If the target class is not public but the targeted method is, we might run into
// visibility problems when rebinding.
if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
// If the original class is public and this method is public, it might have been called
@@ -178,27 +179,37 @@
return null;
}
- private void computeFieldRebinding(Set<DexField> fields,
+ private void computeFieldRebinding(Map<DexField, Set<DexEncodedMethod>> fields,
BiFunction<DexType, DexField, DexEncodedField> lookup,
BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
- for (DexField field : fields) {
+ for (Map.Entry<DexField, Set<DexEncodedMethod>> entry : fields.entrySet()) {
+ DexField field = entry.getKey();
field = lense.lookupField(field, null);
DexEncodedField target = lookup.apply(field.getHolder(), field);
// Rebind to the lowest library class or program class. Do not rebind accesses to fields that
- // are not public, as this might lead to access violation errors.
- if (target != null && target.field != field && isVisibleFromOtherClasses(target)) {
+ // are not visible from the access context.
+ Set<DexEncodedMethod> contexts = entry.getValue();
+ if (target != null && target.field != field && contexts.stream().allMatch(context ->
+ isVisibleFromOriginalContext(context.method.getHolder(), target))) {
builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
}
}
}
- private boolean isVisibleFromOtherClasses(DexEncodedField field) {
- // If the field is not public, the visibility on the class can not be a further constraint.
- if (!field.accessFlags.isPublic()) {
- return true;
+ private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
+ DexType holderType = field.field.getHolder();
+ DexClass holder = appInfo.definitionFor(holderType);
+ if (holder == null) {
+ return false;
}
- // If the field is public, then a non-public holder class will further constrain visibility.
- return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
+ Constraint classVisibility =
+ Constraint.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
+ if (classVisibility == Constraint.NEVER) {
+ return false;
+ }
+ Constraint fieldVisibility =
+ Constraint.deriveConstraint(context, holderType, field.accessFlags, appInfo);
+ return fieldVisibility != Constraint.NEVER;
}
public GraphLense run() {
@@ -213,10 +224,15 @@
// Likewise static invokes.
computeMethodRebinding(appInfo.staticInvokes, this::anyLookup);
- computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
+ computeFieldRebinding(appInfo.staticFieldReads,
appInfo::resolveFieldOn, DexClass::lookupField);
- computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
+ computeFieldRebinding(appInfo.staticFieldWrites,
appInfo::resolveFieldOn, DexClass::lookupField);
+ computeFieldRebinding(appInfo.instanceFieldReads,
+ appInfo::resolveFieldOn, DexClass::lookupField);
+ computeFieldRebinding(appInfo.instanceFieldWrites,
+ appInfo::resolveFieldOn, DexClass::lookupField);
+
return builder.build(appInfo.dexItemFactory, lense);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a726391..2774709 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -99,10 +99,14 @@
Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsRead =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsWritten =
+ Maps.newIdentityHashMap();
private final ProtoLiteExtension protoLiteExtension;
private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
@@ -361,7 +365,7 @@
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- if (!registerItemWithTarget(instanceFieldsWritten, field)) {
+ if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -374,7 +378,7 @@
@Override
public boolean registerInstanceFieldRead(DexField field) {
- if (!registerItemWithTarget(instanceFieldsRead, field)) {
+ if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -392,7 +396,7 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
- if (!registerItemWithTarget(staticFieldsRead, field)) {
+ if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -404,7 +408,7 @@
@Override
public boolean registerStaticFieldWrite(DexField field) {
- if (!registerItemWithTarget(staticFieldsWritten, field)) {
+ if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -1208,34 +1212,41 @@
}
}
- private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
- return map.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toCollection(Sets::newIdentityHashSet));
+ private Map<DexField, Set<DexEncodedMethod>> collectFields(
+ Map<DexType, Set<TargetWithContext<DexField>>> map) {
+ Map<DexField, Set<DexEncodedMethod>> result = new IdentityHashMap<>();
+ for (Map.Entry<DexType, Set<TargetWithContext<DexField>>> entry : map.entrySet()) {
+ for (TargetWithContext<DexField> fieldWithContext : entry.getValue()) {
+ DexField field = fieldWithContext.getTarget();
+ DexEncodedMethod context = fieldWithContext.getContext();
+ result.computeIfAbsent(field, k -> Sets.newIdentityHashSet())
+ .add(context);
+ }
+ }
+ return result;
}
- SortedSet<DexField> collectInstanceFieldsRead() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsRead));
+ Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsRead() {
+ return Collections.unmodifiableMap(collectFields(instanceFieldsRead));
}
- SortedSet<DexField> collectInstanceFieldsWritten() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsWritten));
+ Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsWritten() {
+ return Collections.unmodifiableMap(collectFields(instanceFieldsWritten));
}
- SortedSet<DexField> collectStaticFieldsRead() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsRead));
+ Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsRead() {
+ return Collections.unmodifiableMap(collectFields(staticFieldsRead));
}
- SortedSet<DexField> collectStaticFieldsWritten() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsWritten));
+ Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsWritten() {
+ return Collections.unmodifiableMap(collectFields(staticFieldsWritten));
}
- private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
- Function<DexField, DexField> lookup) {
- return map.values().stream().flatMap(set -> set.stream().map(lookup).filter(Objects::nonNull))
+ private Set<DexField> collectReachedFields(
+ Set<DexField> set, Function<DexField, DexField> lookup) {
+ return set.stream()
+ .map(lookup)
+ .filter(Objects::nonNull)
.collect(Collectors.toCollection(Sets::newIdentityHashSet));
}
@@ -1249,16 +1260,11 @@
return target == null ? null : target.field;
}
- SortedSet<DexField> collectFieldsRead() {
+ SortedSet<DexField> mergeFieldAccesses(Set<DexField> instanceFields, Set<DexField> staticFields) {
return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
- Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
- collectReachedFields(staticFieldsRead, this::tryLookupStaticField)));
- }
-
- SortedSet<DexField> collectFieldsWritten() {
- return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
- Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
- collectReachedFields(staticFieldsWritten, this::tryLookupStaticField)));
+ Sets.union(
+ collectReachedFields(instanceFields, this::tryLookupInstanceField),
+ collectReachedFields(staticFields, this::tryLookupStaticField)));
}
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
@@ -1429,21 +1435,21 @@
*/
public final SortedSet<DexField> fieldsWritten;
/**
- * Set of all field ids used in instance field reads.
+ * Set of all field ids used in instance field reads, along with access context.
*/
- public final SortedSet<DexField> instanceFieldReads;
+ public final Map<DexField, Set<DexEncodedMethod>> instanceFieldReads;
/**
- * Set of all field ids used in instance field writes.
+ * Set of all field ids used in instance field writes, along with access context.
*/
- public final SortedSet<DexField> instanceFieldWrites;
+ public final Map<DexField, Set<DexEncodedMethod>> instanceFieldWrites;
/**
- * Set of all field ids used in static static field reads.
+ * Set of all field ids used in static static field reads, along with access context.
*/
- public final SortedSet<DexField> staticFieldReads;
+ public final Map<DexField, Set<DexEncodedMethod>> staticFieldReads;
/**
- * Set of all field ids used in static field writes.
+ * Set of all field ids used in static field writes, along with access context.
*/
- public final SortedSet<DexField> staticFieldWrites;
+ public final Map<DexField, Set<DexEncodedMethod>> staticFieldWrites;
/**
* Set of all methods referenced in virtual invokes;
*/
@@ -1515,8 +1521,10 @@
this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
this.staticFieldReads = enqueuer.collectStaticFieldsRead();
this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
- this.fieldsRead = enqueuer.collectFieldsRead();
- this.fieldsWritten = enqueuer.collectFieldsWritten();
+ this.fieldsRead = enqueuer.mergeFieldAccesses(
+ instanceFieldReads.keySet(), staticFieldReads.keySet());
+ this.fieldsWritten = enqueuer.mergeFieldAccesses(
+ instanceFieldWrites.keySet(), staticFieldWrites.keySet());
this.pinnedItems = rewritePinnedItemsToDescriptors(enqueuer.pinnedItems);
this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
@@ -1532,8 +1540,8 @@
this.prunedTypes = Collections.emptySet();
this.switchMaps = Collections.emptyMap();
this.ordinalsMaps = Collections.emptyMap();
- assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application,
@@ -1566,8 +1574,8 @@
this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
this.switchMaps = previous.switchMaps;
this.ordinalsMaps = previous.ordinalsMaps;
- assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1579,10 +1587,14 @@
this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
- this.instanceFieldReads = rewriteItems(previous.instanceFieldReads, lense::lookupField);
- this.instanceFieldWrites = rewriteItems(previous.instanceFieldWrites, lense::lookupField);
- this.staticFieldReads = rewriteItems(previous.staticFieldReads, lense::lookupField);
- this.staticFieldWrites = rewriteItems(previous.staticFieldWrites, lense::lookupField);
+ this.instanceFieldReads =
+ rewriteKeysWhileMergingValues(previous.instanceFieldReads, lense::lookupField);
+ this.instanceFieldWrites =
+ rewriteKeysWhileMergingValues(previous.instanceFieldWrites, lense::lookupField);
+ this.staticFieldReads =
+ rewriteKeysWhileMergingValues(previous.staticFieldReads, lense::lookupField);
+ this.staticFieldWrites =
+ rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
@@ -1608,8 +1620,8 @@
this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
this.protoLiteFields = previous.protoLiteFields;
// Sanity check sets after rewriting.
- assert Sets.intersection(instanceFieldReads, staticFieldReads).isEmpty();
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).isEmpty();
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
public AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1744,6 +1756,18 @@
return builder.build();
}
+ private static <T extends PresortedComparable<T>, S> Map<T, Set<S>>
+ rewriteKeysWhileMergingValues(
+ Map<T, Set<S>> original, BiFunction<T, DexEncodedMethod, T> rewrite) {
+ Map<T, Set<S>> result = new IdentityHashMap<>();
+ for (T item : original.keySet()) {
+ T rewrittenKey = rewrite.apply(item, null);
+ result.computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
+ .addAll(original.get(item));
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
private static ImmutableSet<DexItem> rewriteMixedItems(
Set<DexItem> original, GraphLense lense) {
ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
@@ -2095,6 +2119,10 @@
return target;
}
+ public DexEncodedMethod getContext() {
+ return context;
+ }
+
@Override
public int hashCode() {
return target.hashCode() * 31 + context.hashCode();
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 4d7240e..923398d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -550,24 +550,6 @@
// Tests where the output of R8 fails when run with Art.
private static final Multimap<String, TestCondition> failingRunWithArt =
new ImmutableListMultimap.Builder<String, TestCondition>()
- // This test relies on specific field access patterns, which we rewrite.
- .put("064-field-access",
- TestCondition.match(
- TestCondition.R8DEX_NOT_AFTER_D8_COMPILER,
- TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
- .put("064-field-access",
- TestCondition.match(
- TestCondition.R8DEX_COMPILER,
- TestCondition.runtimes(
- DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
- DexVm.Version.V5_1_1)))
- .put("064-field-access",
- TestCondition.match(
- TestCondition.tools(DexTool.NONE),
- TestCondition.D8_AFTER_R8CF_COMPILER,
- TestCondition.runtimes(
- DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
- DexVm.Version.V5_1_1)))
// The growth limit test fails after processing by R8 because R8 will eliminate an
// "unneeded" const store. The following reflective call to the VM's GC will then see the
// large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java
new file mode 100644
index 0000000..bac896e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java
@@ -0,0 +1,179 @@
+// Copyright (c) 2018, 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.cf;
+
+import java.util.*;
+import org.objectweb.asm.*;
+
+public class UninitializedInFrameDump implements Opcodes {
+
+ public static byte[] dump() throws Exception {
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ FieldVisitor fv;
+ MethodVisitor mv;
+ AnnotationVisitor av0;
+
+ cw.visit(
+ V1_8,
+ ACC_PUBLIC + ACC_SUPER,
+ "com/android/tools/r8/cf/UninitializedInFrameTest",
+ null,
+ "java/lang/Object",
+ null);
+
+ // The constructor UninitializedInFrameTest(int i) has been modified
+ // to add a jump back to the entry block.
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(I)V", null, null);
+ mv.visitCode();
+ Label l = new Label(); // Added
+ mv.visitLabel(l); // Added
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(ICONST_1);
+ mv.visitInsn(ISUB);
+ mv.visitInsn(DUP); // Added
+ mv.visitIntInsn(BIPUSH, 42);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IF_ICMPLT, l0);
+ // mv.visitInsn(ICONST_1);
+ mv.visitVarInsn(ISTORE, 1); // Added
+ mv.visitInsn(POP); // Added
+ mv.visitJumpInsn(GOTO, l); // Added
+ Label l1 = new Label();
+ mv.visitJumpInsn(GOTO, l1);
+ mv.visitLabel(l0);
+ mv.visitInsn(POP); // Added
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(l1);
+ mv.visitMethodInsn(
+ INVOKESPECIAL,
+ "com/android/tools/r8/cf/UninitializedInFrameTest",
+ "<init>",
+ "(Z)V",
+ false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ // mv.visitMaxs(3, 2);
+ mv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_PRIVATE, "<init>", "(Z)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ // mv.visitMaxs(1, 2);
+ mv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFEQ, l0);
+ mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitIntInsn(BIPUSH, 42);
+ Label l1 = new Label();
+ mv.visitJumpInsn(IF_ICMPNE, l1);
+ // mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+ // mv.visitInsn(DUP);
+ mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "()V", false);
+ mv.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/Throwable;)V", false);
+ mv.visitVarInsn(ASTORE, 1);
+ // At this point, stack is empty.
+ Label l2 = new Label();
+ mv.visitJumpInsn(GOTO, l2);
+ mv.visitLabel(l1);
+ // At this point, stack contains two copies of uninitialized RuntimeException.
+ // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ // mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+ // mv.visitInsn(DUP);
+ mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ mv.visitLdcInsn("You supplied ");
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(I)Ljava/lang/StringBuilder;",
+ false);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitInsn(ICONST_1);
+ Label l3 = new Label();
+ mv.visitJumpInsn(IF_ICMPNE, l3);
+ mv.visitLdcInsn(" arg");
+ Label l4 = new Label();
+ mv.visitJumpInsn(GOTO, l4);
+ mv.visitLabel(l3);
+ // At this point, stack contains two copies of uninitialized RuntimeException.
+ // Note that asmifier seems to produce incorrect labels for the uninitialized type.
+ // mv.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 3, new Object[] {l1,
+ // l1, "java/lang/StringBuilder"});
+ mv.visitLdcInsn(" args");
+ mv.visitLabel(l4);
+ // At this point, stack contains two copies of uninitialized RuntimeException.
+ // mv.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 4, new Object[] {l1,
+ // l1, "java/lang/StringBuilder", "java/lang/String"});
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ mv.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false);
+ mv.visitVarInsn(ASTORE, 1);
+ mv.visitLabel(l2);
+ // At this point, stack is empty, and local 1 contains an initialized RuntimeException.
+ // mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {"java/lang/RuntimeException"}, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitInsn(ICONST_2);
+ mv.visitInsn(IREM);
+ Label l5 = new Label();
+ mv.visitJumpInsn(IFNE, l5);
+ mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
+ mv.visitLabel(l5);
+ // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(l0);
+ // mv.visitFrame(Opcodes.F_CHOP,1, null, 0, null);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ // mv.visitMaxs(5, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java
new file mode 100644
index 0000000..4479796
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2018, 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.cf;
+
+public class UninitializedInFrameTest {
+ int v;
+
+ public UninitializedInFrameTest(int i) {
+ // In UninitializedInFrameDump, this method is changed to:
+ // while (i-1 >= 42) {i = i-1;} this(i-1 >= 42);
+ // ...which is invalid in Java source since this() must be the first statement.
+ // Put "i-1 >= 42" in the code here to aid the manual editing in UninitializedInFrameDump.
+ this(i - 1 >= 42);
+ }
+
+ public UninitializedInFrameTest(boolean b) {
+ v = b ? 42 : 0;
+ System.out.println(this);
+ // Add an InvokeDirect that has 'this' as argument to ensure we don't consider it to be
+ // an initialization for 'this'.
+ if (!b) {
+ throw new AssertionError(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Hello world! " + v;
+ }
+
+ public static void main(String[] args) {
+ try {
+ new UninitializedInFrameTest(true);
+ new UninitializedInFrameTest(45);
+ } catch (AssertionError e) {
+ }
+ if (args.length != 0) {
+ RuntimeException e;
+ if (args.length == 42) {
+ e = new RuntimeException(new IllegalArgumentException());
+ } else {
+ e =
+ new RuntimeException(
+ "You supplied " + args.length + (args.length == 1 ? " arg" : " args"));
+ }
+ if (args.length % 2 == 0) {
+ System.out.println(e);
+ }
+ throw e;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
new file mode 100644
index 0000000..f433a9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2018, 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.cf;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class UninitializedInFrameTestRunner {
+ static final Class CLASS = UninitializedInFrameTest.class;
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void test() throws Exception {
+ test(ToolHelper.getClassAsBytes(CLASS));
+ }
+
+ @Test
+ public void testDump() throws Exception {
+ test(UninitializedInFrameDump.dump());
+ }
+
+ private void test(byte[] clazz) throws CompilationFailedException, IOException {
+ Path input = temp.getRoot().toPath().resolve("input.jar");
+ Path output = temp.getRoot().toPath().resolve("output.jar");
+
+ ArchiveConsumer inputConsumer = new ArchiveConsumer(input);
+ inputConsumer.accept(clazz, DescriptorUtils.javaTypeToDescriptor(CLASS.getName()), null);
+ inputConsumer.finished(null);
+ ProcessResult runInput = ToolHelper.runJava(input, CLASS.getCanonicalName());
+ if (runInput.exitCode != 0) {
+ System.out.println(runInput);
+ }
+ Assert.assertEquals(0, runInput.exitCode);
+
+ R8.run(
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addClassProgramData(clazz, Origin.unknown())
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(new ArchiveConsumer(output))
+ .build());
+ ProcessResult runOutput = ToolHelper.runJava(output, CLASS.getCanonicalName());
+ if (runOutput.exitCode != 0) {
+ System.out.println(runOutput);
+ }
+ Assert.assertEquals(runInput.toString(), runOutput.toString());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 19a5ee2..cf87d46 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -89,9 +89,9 @@
if (useOptions) {
Options options = new Options();
- options.inputArchives.add(inputZip.toString());
- options.featureSplitMapping = splitSpec.toString();
- options.splitBaseName = output.toString();
+ options.addInputArchive(inputZip.toString());
+ options.setFeatureSplitMapping(splitSpec.toString());
+ options.setSplitBaseName(output.toString());
DexSplitter.run(options);
} else {
DexSplitter.main(
@@ -235,12 +235,12 @@
featureStream.close();
if (useOptions) {
Options options = new Options();
- options.inputArchives.add(inputZip.toString());
- options.splitBaseName = output.toString();
+ options.addInputArchive(inputZip.toString());
+ options.setSplitBaseName(output.toString());
if (explicitBase) {
- options.featureJars.add(baseJar.toString());
+ options.addFeatureJar(baseJar.toString());
}
- options.featureJars.add(featureJar.toString());
+ options.addFeatureJar(featureJar.toString());
DexSplitter.run(options);
} else {
List<String> args = Lists.newArrayList(
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
new file mode 100644
index 0000000..440f4f3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
@@ -0,0 +1,820 @@
+// Copyright (c) 2018, 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.regalloc;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import java.util.List;
+import java.util.Map;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class B77240639 extends TestBase {
+ @Ignore("b/77240639")
+ @Test
+ public void test() throws Exception {
+ AndroidApp app = compileWithD8(readClasses(TestClass.class));
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazz = inspector.clazz(TestClass.class);
+ assertThat(clazz, isPresent());
+ }
+}
+
+class TestClass {
+ private boolean b;
+ private boolean b_flag = false;
+ private Boolean ob;
+ private boolean ob_flag = false;
+ private List<Boolean> vb;
+ private boolean vb_flag = false;
+ private List<Boolean> vob;
+ private boolean vob_flag = false;
+ private Map<String, Boolean> db;
+ private boolean db_flag = false;
+ private Map<String, Boolean> dob;
+ private boolean dob_flag = false;
+ private int i;
+ private boolean i_flag = false;
+ private Integer oi;
+ private boolean oi_flag = false;
+ private List<Boolean> vi;
+ private boolean vi_flag = false;
+ private List<Boolean> voi;
+ private boolean voi_flag = false;
+ private Map<String, Boolean> di;
+ private boolean di_flag = false;
+ private Map<String, Boolean> doi;
+ private boolean doi_flag = false;
+ private int ui;
+ private boolean ui_flag = false;
+ private Integer oui;
+ private boolean oui_flag = false;
+ private List<Boolean> vui;
+ private boolean vui_flag = false;
+ private List<Boolean> voui;
+ private boolean voui_flag = false;
+ private Map<String, Boolean> dui;
+ private boolean dui_flag = false;
+ private Map<String, Boolean> doui;
+ private boolean doui_flag = false;
+ private long i64;
+ private boolean i64_flag = false;
+ private Long oi64;
+ private boolean oi64_flag = false;
+ private List<Boolean> vi64;
+ private boolean vi64_flag = false;
+ private List<Boolean> voi64;
+ private boolean voi64_flag = false;
+ private Map<String, Boolean> di64;
+ private boolean di64_flag = false;
+ private Map<String, Boolean> doi64;
+ private boolean doi64_flag = false;
+ private float fl;
+ private boolean fl_flag = false;
+ private Float ofl;
+ private boolean ofl_flag = false;
+ private List<Boolean> vfl;
+ private boolean vfl_flag = false;
+ private List<Boolean> vofl;
+ private boolean vofl_flag = false;
+ private Map<String, Boolean> dfl;
+ private boolean dfl_flag = false;
+ private Map<String, Boolean> dofl;
+ private boolean dofl_flag = false;
+ private double d;
+ private boolean d_flag = false;
+ private Double od;
+ private boolean od_flag = false;
+ private List<Boolean> vd;
+ private boolean vd_flag = false;
+ private List<Boolean> vod;
+ private boolean vod_flag = false;
+ private Map<String, Boolean> dd;
+ private boolean dd_flag = false;
+ private Map<String, Boolean> dod;
+ private boolean dod_flag = false;
+ private String s;
+ private boolean s_flag = false;
+ private String os;
+ private boolean os_flag = false;
+ private List<Boolean> vs;
+ private boolean vs_flag = false;
+ private List<Boolean> vos;
+ private boolean vos_flag = false;
+ private Map<String, Boolean> ds;
+ private boolean ds_flag = false;
+ private Map<String, Boolean> dos;
+ private boolean dos_flag = false;
+ private long ti;
+ private boolean ti_flag = false;
+ private Long oti;
+ private boolean oti_flag = false;
+ private List<Boolean> vti;
+ private boolean vti_flag = false;
+ private List<Boolean> voti;
+ private boolean voti_flag = false;
+ private Map<String, Boolean> dti;
+ private boolean dti_flag = false;
+ private Map<String, Boolean> doti;
+ private boolean doti_flag = false;
+ private long at;
+ private boolean at_flag = false;
+ private Long oat;
+ private boolean oat_flag = false;
+ private List<Boolean> vat;
+ private boolean vat_flag = false;
+ private List<Boolean> voat;
+ private boolean voat_flag = false;
+ private Map<String, Boolean> dat;
+ private boolean dat_flag = false;
+ private Map<String, Boolean> doat;
+ private boolean doat_flag = false;
+ private long rt;
+ private boolean rt_flag = false;
+ private Long ort;
+ private boolean ort_flag = false;
+ private List<Boolean> vrt;
+ private boolean vrt_flag = false;
+ private List<Boolean> vort;
+ private boolean vort_flag = false;
+ private Map<String, Boolean> drt;
+ private boolean drt_flag = false;
+ private Map<String, Boolean> dort;
+ private boolean dort_flag = false;
+ private byte[] by;
+ private boolean by_flag = false;
+ private byte[] oby;
+ private boolean oby_flag = false;
+ private int c;
+ private boolean c_flag = false;
+ private Integer oc;
+ private boolean oc_flag = false;
+ private List<Integer> vc;
+ private boolean vc_flag = false;
+ private List<Integer> voc;
+ private boolean voc_flag = false;
+ private Map<String, Integer> dc;
+ private boolean dc_flag = false;
+ private Map<String, Integer> doc;
+ private boolean doc_flag = false;
+ private Object p;
+ private boolean p_flag = false;
+ private Object op;
+ private boolean op_flag = false;
+ private List<Object> vp;
+ private boolean vp_flag = false;
+ private List<Object> vop;
+ private boolean vop_flag = false;
+ private Map<String, Object> dp;
+ private boolean dp_flag = false;
+ private Map<String, Object> dop;
+ private boolean dop_flag = false;
+ private Object e;
+ private boolean e_flag = false;
+ private Object oe;
+ private boolean oe_flag = false;
+ private List<Object> ve;
+ private boolean ve_flag = false;
+ private List<Object> voe;
+ private boolean voe_flag = false;
+ private Map<String, Object> de;
+ private boolean de_flag = false;
+ private Map<String, Object> doe;
+ private boolean doe_flag = false;
+ private int be;
+ private boolean be_flag = false;
+ private Integer obe;
+ private boolean obe_flag = false;
+ private List<Object> vbe;
+ private boolean vbe_flag = false;
+ private List<Object> vobe;
+ private boolean vobe_flag = false;
+ private Map<String, Object> dbe;
+ private boolean dbe_flag = false;
+ private Map<String, Object> dobe;
+ private boolean dobe_flag = false;
+ private Object ts;
+ private boolean ts_flag = false;
+ private Object ots;
+ private boolean ots_flag = false;
+ private List<Object> vts;
+ private boolean vts_flag = false;
+ private List<Object> vots;
+ private boolean vots_flag = false;
+ private Map<String, Object> dts;
+ private boolean dts_flag = false;
+ private Map<String, Object> dots;
+ private boolean dots_flag = false;
+ private Object lts;
+ private boolean lts_flag = false;
+ private Object olts;
+ private boolean olts_flag = false;
+ private List<Object> vlts;
+ private boolean vlts_flag = false;
+ private List<Object> volts;
+ private boolean volts_flag = false;
+ private Map<String, Object> dlts;
+ private boolean dlts_flag = false;
+ private Map<String, Object> dolts;
+ private boolean dolts_flag = false;
+ private Object opts;
+ private boolean opts_flag = false;
+ private Object oopts;
+ private boolean oopts_flag = false;
+ private List<Object> vopts;
+ private boolean vopts_flag = false;
+ private List<Object> voopts;
+ private boolean voopts_flag = false;
+ private Map<String, Object> dopts;
+ private boolean dopts_flag = false;
+ private Map<String, Object> doopts;
+ private boolean doopts_flag = false;
+ private Object nativeObject;
+
+ public TestClass() {}
+
+ public TestClass(
+ boolean b,
+ Boolean ob,
+ List<Boolean> vb,
+ List<Boolean> vob,
+ Map<String, Boolean> db,
+ Map<String, Boolean> dob,
+ int i,
+ Integer oi,
+ List<Boolean> vi,
+ List<Boolean> voi,
+ Map<String, Boolean> di,
+ Map<String, Boolean> doi,
+ int ui,
+ Integer oui,
+ List<Boolean> vui,
+ List<Boolean> voui,
+ Map<String, Boolean> dui,
+ Map<String, Boolean> doui,
+ long i64,
+ Long oi64,
+ List<Boolean> vi64,
+ List<Boolean> voi64,
+ Map<String, Boolean> di64,
+ Map<String, Boolean> doi64,
+ float fl,
+ Float ofl,
+ List<Boolean> vfl,
+ List<Boolean> vofl,
+ Map<String, Boolean> dfl,
+ Map<String, Boolean> dofl,
+ double d,
+ Double od,
+ List<Boolean> vd,
+ List<Boolean> vod,
+ Map<String, Boolean> dd,
+ Map<String, Boolean> dod,
+ String s,
+ String os,
+ List<Boolean> vs,
+ List<Boolean> vos,
+ Map<String, Boolean> ds,
+ Map<String, Boolean> dos,
+ long ti,
+ Long oti,
+ List<Boolean> vti,
+ List<Boolean> voti,
+ Map<String, Boolean> dti,
+ Map<String, Boolean> doti,
+ long at,
+ Long oat,
+ List<Boolean> vat,
+ List<Boolean> voat,
+ Map<String, Boolean> dat,
+ Map<String, Boolean> doat,
+ long rt,
+ Long ort,
+ List<Boolean> vrt,
+ List<Boolean> vort,
+ Map<String, Boolean> drt,
+ Map<String, Boolean> dort,
+ byte[] by,
+ byte[] oby,
+ int c,
+ Integer oc,
+ List<Integer> vc,
+ List<Integer> voc,
+ Map<String, Integer> dc,
+ Map<String, Integer> doc,
+ Object p,
+ Object op,
+ List<Object> vp,
+ List<Object> vop,
+ Map<String, Object> dp,
+ Map<String, Object> dop,
+ Object e,
+ Object oe,
+ List<Object> ve,
+ List<Object> voe,
+ Map<String, Object> de,
+ Map<String, Object> doe,
+ int be,
+ Integer obe,
+ List<Object> vbe,
+ List<Object> vobe,
+ Map<String, Object> dbe,
+ Map<String, Object> dobe,
+ Object ts,
+ Object ots,
+ List<Object> vts,
+ List<Object> vots,
+ Map<String, Object> dts,
+ Map<String, Object> dots,
+ Object lts,
+ Object olts,
+ List<Object> vlts,
+ List<Object> volts,
+ Map<String, Object> dlts,
+ Map<String, Object> dolts,
+ Object opts,
+ Object oopts,
+ List<Object> vopts,
+ List<Object> voopts,
+ Map<String, Object> dopts,
+ Map<String, Object> doopts) {
+ if (vb == null) {
+ throw new IllegalArgumentException("vb");
+ } else if (vob == null) {
+ throw new IllegalArgumentException("vob");
+ } else if (db == null) {
+ throw new IllegalArgumentException("db");
+ } else if (dob == null) {
+ throw new IllegalArgumentException("dob");
+ } else if (vi == null) {
+ throw new IllegalArgumentException("vi");
+ } else if (voi == null) {
+ throw new IllegalArgumentException("voi");
+ } else if (di == null) {
+ throw new IllegalArgumentException("di");
+ } else if (doi == null) {
+ throw new IllegalArgumentException("doi");
+ } else if (vui == null) {
+ throw new IllegalArgumentException("vui");
+ } else if (voui == null) {
+ throw new IllegalArgumentException("voui");
+ } else if (dui == null) {
+ throw new IllegalArgumentException("dui");
+ } else if (doui == null) {
+ throw new IllegalArgumentException("doui");
+ } else if (vi64 == null) {
+ throw new IllegalArgumentException("vi64");
+ } else if (voi64 == null) {
+ throw new IllegalArgumentException("voi64");
+ } else if (di64 == null) {
+ throw new IllegalArgumentException("di64");
+ } else if (doi64 == null) {
+ throw new IllegalArgumentException("doi64");
+ } else if (vfl == null) {
+ throw new IllegalArgumentException("vfl");
+ } else if (vofl == null) {
+ throw new IllegalArgumentException("vofl");
+ } else if (dfl == null) {
+ throw new IllegalArgumentException("dfl");
+ } else if (dofl == null) {
+ throw new IllegalArgumentException("dofl");
+ } else if (vd == null) {
+ throw new IllegalArgumentException("vd");
+ } else if (vod == null) {
+ throw new IllegalArgumentException("vod");
+ } else if (dd == null) {
+ throw new IllegalArgumentException("dd");
+ } else if (dod == null) {
+ throw new IllegalArgumentException("dod");
+ } else if (s == null) {
+ throw new IllegalArgumentException("s");
+ } else if (vs == null) {
+ throw new IllegalArgumentException("vs");
+ } else if (vos == null) {
+ throw new IllegalArgumentException("vos");
+ } else if (ds == null) {
+ throw new IllegalArgumentException("ds");
+ } else if (dos == null) {
+ throw new IllegalArgumentException("dos");
+ } else if (vti == null) {
+ throw new IllegalArgumentException("vti");
+ } else if (voti == null) {
+ throw new IllegalArgumentException("voti");
+ } else if (dti == null) {
+ throw new IllegalArgumentException("dti");
+ } else if (doti == null) {
+ throw new IllegalArgumentException("doti");
+ } else if (vat == null) {
+ throw new IllegalArgumentException("vat");
+ } else if (voat == null) {
+ throw new IllegalArgumentException("voat");
+ } else if (dat == null) {
+ throw new IllegalArgumentException("dat");
+ } else if (doat == null) {
+ throw new IllegalArgumentException("doat");
+ } else if (vrt == null) {
+ throw new IllegalArgumentException("vrt");
+ } else if (vort == null) {
+ throw new IllegalArgumentException("vort");
+ } else if (drt == null) {
+ throw new IllegalArgumentException("drt");
+ } else if (dort == null) {
+ throw new IllegalArgumentException("dort");
+ } else if (by == null) {
+ throw new IllegalArgumentException("by");
+ } else if (vc == null) {
+ throw new IllegalArgumentException("vc");
+ } else if (voc == null) {
+ throw new IllegalArgumentException("voc");
+ } else if (dc == null) {
+ throw new IllegalArgumentException("dc");
+ } else if (doc == null) {
+ throw new IllegalArgumentException("doc");
+ } else if (p == null) {
+ throw new IllegalArgumentException("p");
+ } else if (vp == null) {
+ throw new IllegalArgumentException("vp");
+ } else if (vop == null) {
+ throw new IllegalArgumentException("vop");
+ } else if (dp == null) {
+ throw new IllegalArgumentException("dp");
+ } else if (dop == null) {
+ throw new IllegalArgumentException("dop");
+ } else if (e == null) {
+ throw new IllegalArgumentException("e");
+ } else if (ve == null) {
+ throw new IllegalArgumentException("ve");
+ } else if (voe == null) {
+ throw new IllegalArgumentException("voe");
+ } else if (de == null) {
+ throw new IllegalArgumentException("de");
+ } else if (doe == null) {
+ throw new IllegalArgumentException("doe");
+ } else if (vbe == null) {
+ throw new IllegalArgumentException("vbe");
+ } else if (vobe == null) {
+ throw new IllegalArgumentException("vobe");
+ } else if (dbe == null) {
+ throw new IllegalArgumentException("dbe");
+ } else if (dobe == null) {
+ throw new IllegalArgumentException("dobe");
+ } else if (ts == null) {
+ throw new IllegalArgumentException("ts");
+ } else if (vts == null) {
+ throw new IllegalArgumentException("vts");
+ } else if (vots == null) {
+ throw new IllegalArgumentException("vots");
+ } else if (dts == null) {
+ throw new IllegalArgumentException("dts");
+ } else if (dots == null) {
+ throw new IllegalArgumentException("dots");
+ } else if (lts == null) {
+ throw new IllegalArgumentException("lts");
+ } else if (vlts == null) {
+ throw new IllegalArgumentException("vlts");
+ } else if (volts == null) {
+ throw new IllegalArgumentException("volts");
+ } else if (dlts == null) {
+ throw new IllegalArgumentException("dlts");
+ } else if (dolts == null) {
+ throw new IllegalArgumentException("dolts");
+ } else if (opts == null) {
+ throw new IllegalArgumentException("opts");
+ } else if (vopts == null) {
+ throw new IllegalArgumentException("vopts");
+ } else if (voopts == null) {
+ throw new IllegalArgumentException("voopts");
+ } else if (dopts == null) {
+ throw new IllegalArgumentException("dopts");
+ } else if (doopts == null) {
+ throw new IllegalArgumentException("doopts");
+ } else {
+ this.nativeObject =
+ this.init(
+ b, ob, vb, vob, db, dob, i, oi, vi, voi, di, doi, ui, oui, vui, voui, dui, doui, i64,
+ oi64, vi64, voi64, di64, doi64, fl, ofl, vfl, vofl, dfl, dofl, d, od, vd, vod, dd,
+ dod, s, os, vs, vos, ds, dos, ti, oti, vti, voti, dti, doti, at, oat, vat, voat, dat,
+ doat, rt, ort, vrt, vort, drt, dort, by, oby, c, oc, vc, voc, dc, doc, p, op, vp, vop,
+ dp, dop, e, oe, ve, voe, de, doe, be, obe, vbe, vobe, dbe, dobe, ts, ots, vts, vots,
+ dts, dots, lts, olts, vlts, volts, dlts, dolts, opts, oopts, vopts, voopts, dopts,
+ doopts);
+ this.b = b;
+ this.b_flag = true;
+ this.ob = ob;
+ this.ob_flag = true;
+ this.vb = vb;
+ this.vb_flag = true;
+ this.vob = vob;
+ this.vob_flag = true;
+ this.db = db;
+ this.db_flag = true;
+ this.dob = dob;
+ this.dob_flag = true;
+ this.i = i;
+ this.i_flag = true;
+ this.oi = oi;
+ this.oi_flag = true;
+ this.vi = vi;
+ this.vi_flag = true;
+ this.voi = voi;
+ this.voi_flag = true;
+ this.di = di;
+ this.di_flag = true;
+ this.doi = doi;
+ this.doi_flag = true;
+ this.ui = ui;
+ this.ui_flag = true;
+ this.oui = oui;
+ this.oui_flag = true;
+ this.vui = vui;
+ this.vui_flag = true;
+ this.voui = voui;
+ this.voui_flag = true;
+ this.dui = dui;
+ this.dui_flag = true;
+ this.doui = doui;
+ this.doui_flag = true;
+ this.i64 = i64;
+ this.i64_flag = true;
+ this.oi64 = oi64;
+ this.oi64_flag = true;
+ this.vi64 = vi64;
+ this.vi64_flag = true;
+ this.voi64 = voi64;
+ this.voi64_flag = true;
+ this.di64 = di64;
+ this.di64_flag = true;
+ this.doi64 = doi64;
+ this.doi64_flag = true;
+ this.fl = fl;
+ this.fl_flag = true;
+ this.ofl = ofl;
+ this.ofl_flag = true;
+ this.vfl = vfl;
+ this.vfl_flag = true;
+ this.vofl = vofl;
+ this.vofl_flag = true;
+ this.dfl = dfl;
+ this.dfl_flag = true;
+ this.dofl = dofl;
+ this.dofl_flag = true;
+ this.d = d;
+ this.d_flag = true;
+ this.od = od;
+ this.od_flag = true;
+ this.vd = vd;
+ this.vd_flag = true;
+ this.vod = vod;
+ this.vod_flag = true;
+ this.dd = dd;
+ this.dd_flag = true;
+ this.dod = dod;
+ this.dod_flag = true;
+ this.s = s;
+ this.s_flag = true;
+ this.os = os;
+ this.os_flag = true;
+ this.vs = vs;
+ this.vs_flag = true;
+ this.vos = vos;
+ this.vos_flag = true;
+ this.ds = ds;
+ this.ds_flag = true;
+ this.dos = dos;
+ this.dos_flag = true;
+ this.ti = ti;
+ this.ti_flag = true;
+ this.oti = oti;
+ this.oti_flag = true;
+ this.vti = vti;
+ this.vti_flag = true;
+ this.voti = voti;
+ this.voti_flag = true;
+ this.dti = dti;
+ this.dti_flag = true;
+ this.doti = doti;
+ this.doti_flag = true;
+ this.at = at;
+ this.at_flag = true;
+ this.oat = oat;
+ this.oat_flag = true;
+ this.vat = vat;
+ this.vat_flag = true;
+ this.voat = voat;
+ this.voat_flag = true;
+ this.dat = dat;
+ this.dat_flag = true;
+ this.doat = doat;
+ this.doat_flag = true;
+ this.rt = rt;
+ this.rt_flag = true;
+ this.ort = ort;
+ this.ort_flag = true;
+ this.vrt = vrt;
+ this.vrt_flag = true;
+ this.vort = vort;
+ this.vort_flag = true;
+ this.drt = drt;
+ this.drt_flag = true;
+ this.dort = dort;
+ this.dort_flag = true;
+ this.by = by;
+ this.by_flag = true;
+ this.oby = oby;
+ this.oby_flag = true;
+ this.c = c;
+ this.c_flag = true;
+ this.oc = oc;
+ this.oc_flag = true;
+ this.vc = vc;
+ this.vc_flag = true;
+ this.voc = voc;
+ this.voc_flag = true;
+ this.dc = dc;
+ this.dc_flag = true;
+ this.doc = doc;
+ this.doc_flag = true;
+ this.p = p;
+ this.p_flag = true;
+ this.op = op;
+ this.op_flag = true;
+ this.vp = vp;
+ this.vp_flag = true;
+ this.vop = vop;
+ this.vop_flag = true;
+ this.dp = dp;
+ this.dp_flag = true;
+ this.dop = dop;
+ this.dop_flag = true;
+ this.e = e;
+ this.e_flag = true;
+ this.oe = oe;
+ this.oe_flag = true;
+ this.ve = ve;
+ this.ve_flag = true;
+ this.voe = voe;
+ this.voe_flag = true;
+ this.de = de;
+ this.de_flag = true;
+ this.doe = doe;
+ this.doe_flag = true;
+ this.be = be;
+ this.be_flag = true;
+ this.obe = obe;
+ this.obe_flag = true;
+ this.vbe = vbe;
+ this.vbe_flag = true;
+ this.vobe = vobe;
+ this.vobe_flag = true;
+ this.dbe = dbe;
+ this.dbe_flag = true;
+ this.dobe = dobe;
+ this.dobe_flag = true;
+ this.ts = ts;
+ this.ts_flag = true;
+ this.ots = ots;
+ this.ots_flag = true;
+ this.vts = vts;
+ this.vts_flag = true;
+ this.vots = vots;
+ this.vots_flag = true;
+ this.dts = dts;
+ this.dts_flag = true;
+ this.dots = dots;
+ this.dots_flag = true;
+ this.lts = lts;
+ this.lts_flag = true;
+ this.olts = olts;
+ this.olts_flag = true;
+ this.vlts = vlts;
+ this.vlts_flag = true;
+ this.volts = volts;
+ this.volts_flag = true;
+ this.dlts = dlts;
+ this.dlts_flag = true;
+ this.dolts = dolts;
+ this.dolts_flag = true;
+ this.opts = opts;
+ this.opts_flag = true;
+ this.oopts = oopts;
+ this.oopts_flag = true;
+ this.vopts = vopts;
+ this.vopts_flag = true;
+ this.voopts = voopts;
+ this.voopts_flag = true;
+ this.dopts = dopts;
+ this.dopts_flag = true;
+ this.doopts = doopts;
+ this.doopts_flag = true;
+ }
+ }
+
+ private native Object init(
+ boolean v1,
+ Boolean v2,
+ List<Boolean> v3,
+ List<Boolean> v4,
+ Map<String, Boolean> v5,
+ Map<String, Boolean> v6,
+ int v7, Integer var8,
+ List<Boolean> v9,
+ List<Boolean> v10,
+ Map<String, Boolean> v11,
+ Map<String, Boolean> v12,
+ int v13,
+ Integer v14,
+ List<Boolean> v15,
+ List<Boolean> v16,
+ Map<String, Boolean> v17,
+ Map<String, Boolean> v18,
+ long v19, Long var21,
+ List<Boolean> v22,
+ List<Boolean> v23,
+ Map<String, Boolean> v24,
+ Map<String, Boolean> v25,
+ float v26,
+ Float v27,
+ List<Boolean> v28,
+ List<Boolean> v29,
+ Map<String, Boolean> v30,
+ Map<String, Boolean> v31,
+ double v32,
+ Double v34,
+ List<Boolean> v35,
+ List<Boolean> v36,
+ Map<String, Boolean> v37,
+ Map<String, Boolean> v38,
+ String v39,
+ String v40,
+ List<Boolean> v41,
+ List<Boolean> v42,
+ Map<String, Boolean> v43,
+ Map<String, Boolean> v44,
+ long v45, Long var47,
+ List<Boolean> v48,
+ List<Boolean> v49,
+ Map<String, Boolean> v50,
+ Map<String, Boolean> v51,
+ long v52,
+ Long v54,
+ List<Boolean> v55,
+ List<Boolean> v56,
+ Map<String, Boolean> v57,
+ Map<String, Boolean> v58,
+ long v59,
+ Long v61,
+ List<Boolean> v62,
+ List<Boolean> v63,
+ Map<String, Boolean> v64,
+ Map<String, Boolean> v65,
+ byte[] v66,
+ byte[] v67,
+ int v68,
+ Integer v69,
+ List<Integer> v70,
+ List<Integer> v71,
+ Map<String, Integer> v72,
+ Map<String, Integer> v73,
+ Object v74,
+ Object v75,
+ List<Object> v76,
+ List<Object> v77,
+ Map<String, Object> v78,
+ Map<String, Object> v79,
+ Object v80,
+ Object v81,
+ List<Object> v82,
+ List<Object> v83,
+ Map<String, Object> v84,
+ Map<String, Object> v85,
+ int v86,
+ Integer v87,
+ List<Object> v88,
+ List<Object> v89,
+ Map<String, Object> v90,
+ Map<String, Object> v91,
+ Object v92,
+ Object v93,
+ List<Object> v94,
+ List<Object> v95,
+ Map<String, Object> v96,
+ Map<String, Object> v97,
+ Object v98,
+ Object v99,
+ List<Object> v100,
+ List<Object> v101,
+ Map<String, Object> v102,
+ Map<String, Object> v103,
+ Object v104,
+ Object v105,
+ List<Object> v106,
+ List<Object> v107,
+ Map<String, Object> v108,
+ Map<String, Object> v109);
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
index d25c061..6c054e5 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -24,7 +24,6 @@
public class InterfaceRenamingTestRunner extends TestBase {
static final Class CLASS = InterfaceRenamingTest.class;
static final Class[] CLASSES = InterfaceRenamingTest.CLASSES;
- private boolean bug = false;
@Test
public void testCfNoMinify() throws Exception {
@@ -33,7 +32,6 @@
@Test
public void testCfMinify() throws Exception {
- bug = true;
testCf(MinifyMode.JAVA);
}
@@ -49,7 +47,6 @@
@Test
public void testDexMinify() throws Exception {
- bug = true;
testDex(MinifyMode.JAVA);
}
@@ -65,11 +62,6 @@
Path outCf = temp.getRoot().toPath().resolve("cf.zip");
build(new ClassFileConsumer.ArchiveConsumer(outCf), minify);
ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
- if (bug) {
- assertNotEquals(-1, runCf.stderr.indexOf("java.lang.AbstractMethodError"));
- assertNotEquals(0, runCf.exitCode);
- return;
- }
assertEquals(runInput.toString(), runCf.toString());
}
@@ -79,12 +71,6 @@
assertEquals(0, runInput.exitCode);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex), minify);
- if (bug) {
- ProcessResult runDex = ToolHelper.runArtRaw(outDex.toString(), CLASS.getCanonicalName());
- assertNotEquals(-1, runDex.stderr.indexOf("java.lang.AbstractMethodError"));
- assertNotEquals(0, runDex.exitCode);
- return;
- }
ProcessResult runDex =
ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), CLASS.getCanonicalName());
assertEquals(runInput.stdout, runDex.stdout);
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 9fa694d..767420b 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
@@ -11,50 +13,113 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import org.junit.Ignore;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import regress_76025099.Main;
+import regress_76025099.impl.Impl;
@RunWith(VmTestRunner.class)
public class B76025099 extends TestBase {
-
private static final String PRG =
ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
- private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+ private AndroidApp runR8(AndroidApp app) throws Exception {
R8Command command =
ToolHelper.addProguardConfigurationConsumer(
ToolHelper.prepareR8CommandBuilder(app),
pgConfig -> {
pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ pgConfig.setPrintMappingFile(map);
})
- .addProguardConfiguration(
- ImmutableList.of(keepMainProguardConfiguration(main)),
- Origin.unknown())
- .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(pgConfig)
+ .setOutput(tempRoot.toPath(), OutputMode.DexIndexed)
.build();
return ToolHelper.runR8(command, o -> {
o.enableMinification = false;
});
}
- @Ignore("b/76025099")
+ private File tempRoot;
+ private Path jarPath;
+ private AndroidApp originalApp;
+ private String mainName;
+ private Path pgConfig;
+ private Path map;
+
+ @Before
+ public void setUp() throws Exception {
+ tempRoot = temp.getRoot();
+ jarPath = Paths.get(PRG);
+ originalApp = readJar(jarPath);
+ mainName = Main.class.getCanonicalName();
+ pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
+ String config = keepMainProguardConfiguration(Main.class);
+ config += System.lineSeparator() + "-dontobfuscate";
+ Files.write(pgConfig, config.getBytes());
+ map = File.createTempFile("proguard", ".map", tempRoot).toPath();
+ }
+
@Test
- public void test() throws Exception {
- Path out = temp.getRoot().toPath();
- Path jarPath = Paths.get(PRG);
- String mainName = Main.class.getCanonicalName();
+ public void testProguardAndD8() throws Exception {
+ if (!isRunProguard()) {
+ return;
+ }
+
ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
assertEquals(0, jvmOutput.exitCode);
- AndroidApp processedApp = runR8(readJar(jarPath), Main.class, out);
+
+ Path proguarded =
+ File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
+ ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
+ assertEquals(0, proguardResult.exitCode);
+
+ AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
+ verifyFieldAccess(processedApp, jvmOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
+ assertEquals(0, jvmOutput.exitCode);
+
+ AndroidApp processedApp = runR8(originalApp);
+ verifyFieldAccess(processedApp, jvmOutput);
+ }
+
+ private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
+ throws Exception {
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject impl = inspector.clazz(Impl.class);
+ assertThat(impl, isPresent());
+ MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
+ assertThat(init, isPresent());
+ DexCode dexCode = init.getMethod().getCode().asDexCode();
+ checkInstructions(dexCode, ImmutableList.of(
+ InvokeDirect.class,
+ IputObject.class,
+ ReturnVoid.class
+ ));
+ IputObject iput = (IputObject) dexCode.instructions[1];
+ DexField fld = iput.getField();
+ assertEquals("name", fld.name.toString());
+ assertEquals("regress_76025099.impl.Impl", fld.getHolder().toSourceString());
+
ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
assertEquals(0, artOutput.exitCode);
assertEquals(jvmOutput.stdout, artOutput.stdout);
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
index b13a08e..6642352 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.resolution;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
@@ -46,12 +45,11 @@
assertEquals(0, runInput.exitCode);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex));
- // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
- ProcessResult runDex = ToolHelper.runArtRaw(
+ ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
outDex.toString(), CLASS.getCanonicalName());
- assertNotEquals(runInput.stdout, runDex.stdout);
- assertNotEquals(runInput.exitCode, runDex.exitCode);
- assertNotEquals(
+ assertEquals(runInput.stdout, runDex.stdout);
+ assertEquals(runInput.exitCode, runDex.exitCode);
+ assertEquals(
-1,
runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
}