Merge commit '3e0a120cf508a856c117d8784cd6477340071181' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5e43f29..9d43396 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -982,10 +982,10 @@
}
private static boolean verifyOriginalMethodInDebugInfo(DexCode code, DexMethod originalMethod) {
- if (code.getDebugInfo() == null) {
+ if (code.getDebugInfo() == null || code.getDebugInfo().isPcBasedInfo()) {
return true;
}
- for (DexDebugEvent event : code.getDebugInfo().events) {
+ for (DexDebugEvent event : code.getDebugInfo().asEventBasedInfo().events) {
assert !event.isSetInlineFrame() || event.asSetInlineFrame().hasOuterPosition(originalMethod);
}
return true;
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 f8b10e0..47124c0 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
@@ -532,6 +532,27 @@
}
public CfFrame map(java.util.function.Function<DexType, DexType> func) {
+ boolean mapped = false;
+ for (int var : locals.keySet()) {
+ CfFrame.FrameType originalType = locals.get(var);
+ CfFrame.FrameType mappedType = originalType.map(func);
+ mapped = originalType != mappedType;
+ if (mapped) {
+ break;
+ }
+ }
+ if (!mapped) {
+ for (FrameType frameType : stack) {
+ CfFrame.FrameType mappedType = frameType.map(func);
+ mapped = frameType != mappedType;
+ if (mapped) {
+ break;
+ }
+ }
+ }
+ if (!mapped) {
+ return this;
+ }
Int2ReferenceSortedMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
for (int var : locals.keySet()) {
newLocals.put(var, locals.get(var).map(func));
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index f99eee5..5d26e0a 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -26,7 +26,7 @@
import com.android.tools.r8.graph.DexAnnotationDirectory;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfoForWriting;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -132,7 +132,7 @@
}
@Override
- public boolean add(DexDebugInfo dexDebugInfo) {
+ public boolean add(DexDebugInfoForWriting dexDebugInfo) {
return true;
}
@@ -488,7 +488,7 @@
timing.end();
timing.begin("Write bytes");
- ByteBufferResult result = writeDexFile(objectMapping, byteBufferProvider);
+ ByteBufferResult result = writeDexFile(objectMapping, byteBufferProvider, timing);
ByteDataView data =
new ByteDataView(result.buffer.array(), result.buffer.arrayOffset(), result.length);
timing.end();
@@ -789,8 +789,7 @@
}
private ByteBufferResult writeDexFile(
- ObjectToOffsetMapping objectMapping,
- ByteBufferProvider provider) {
+ ObjectToOffsetMapping objectMapping, ByteBufferProvider provider, Timing timing) {
FileWriter fileWriter =
new FileWriter(
provider,
@@ -800,9 +799,9 @@
namingLens,
desugaredLibraryCodeToKeep);
// Collect the non-fixed sections.
- fileWriter.collect();
+ timing.scope("collect", () -> fileWriter.collect());
// Generate and write the bytes.
- return fileWriter.generate();
+ return timing.scope("generate", () -> fileWriter.generate());
}
private static String mapMainDexListName(DexType type, NamingLens namingLens) {
diff --git a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
index 2c3e18a..5a6a377 100644
--- a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
@@ -3,8 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
-import com.android.tools.r8.graph.DexDebugEvent;
-import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfoForWriting;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -17,32 +16,19 @@
private final ObjectToOffsetMapping mapping;
private final GraphLens graphLens;
- private final DexDebugInfo info;
+ private final DexDebugInfoForWriting info;
private ByteBuffer buffer;
public DebugBytecodeWriter(
- DexDebugInfo info, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+ DexDebugInfoForWriting info, ObjectToOffsetMapping mapping, GraphLens graphLens) {
this.info = info;
this.mapping = mapping;
this.graphLens = graphLens;
- // Never allocate a zero-sized buffer, as we need to write the header, and the growth policy
- // requires it to have a positive capacity.
- this.buffer = ByteBuffer.allocate(info.events.length * 5 + 4);
+ this.buffer = ByteBuffer.allocate(info.estimatedWriteSize());
}
public byte[] generate() {
- // Header.
- putUleb128(info.startLine); // line_start
- putUleb128(info.parameters.length);
- for (DexString name : info.parameters) {
- putString(name);
- }
- // Body.
- for (DexDebugEvent event : info.events) {
- event.writeOn(this, mapping, graphLens);
- }
- // Tail.
- putByte(Constants.DBG_END_SEQUENCE);
+ info.write(this, mapping, graphLens);
return Arrays.copyOf(buffer.array(), buffer.position());
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 43a9a57..7a3ccf5 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -25,7 +25,10 @@
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.PcBasedDebugInfo;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedField;
@@ -522,22 +525,28 @@
private DexDebugInfo parseDebugInfo() {
int start = dexReader.getUleb128();
+ boolean isPcBasedDebugInfo = start == 0;
int parametersSize = dexReader.getUleb128();
DexString[] parameters = new DexString[parametersSize];
for (int i = 0; i < parametersSize; i++) {
int index = dexReader.getUleb128p1();
if (index != NO_INDEX) {
parameters[i] = indexedItems.getString(index);
+ isPcBasedDebugInfo = false;
}
}
List<DexDebugEvent> events = new ArrayList<>();
- for (int head = dexReader.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = dexReader.getUbyte()) {
+ for (int head = dexReader.getUbyte();
+ head != Constants.DBG_END_SEQUENCE;
+ head = dexReader.getUbyte()) {
switch (head) {
case Constants.DBG_ADVANCE_PC:
events.add(dexItemFactory.createAdvancePC(dexReader.getUleb128()));
+ isPcBasedDebugInfo = false;
break;
case Constants.DBG_ADVANCE_LINE:
events.add(dexItemFactory.createAdvanceLine(dexReader.getSleb128()));
+ isPcBasedDebugInfo = false;
break;
case Constants.DBG_START_LOCAL: {
int registerNum = dexReader.getUleb128();
@@ -548,6 +557,7 @@
nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
null));
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_START_LOCAL_EXTENDED: {
@@ -560,22 +570,27 @@
nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
sigIdx == NO_INDEX ? null : indexedItems.getString(sigIdx)));
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_END_LOCAL: {
events.add(dexItemFactory.createEndLocal(dexReader.getUleb128()));
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_RESTART_LOCAL: {
events.add(dexItemFactory.createRestartLocal(dexReader.getUleb128()));
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_SET_PROLOGUE_END: {
events.add(dexItemFactory.createSetPrologueEnd());
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_SET_EPILOGUE_BEGIN: {
events.add(dexItemFactory.createSetEpilogueBegin());
+ isPcBasedDebugInfo = false;
break;
}
case Constants.DBG_SET_FILE: {
@@ -584,15 +599,26 @@
if (options.readDebugSetFileEvent) {
events.add(dexItemFactory.createSetFile(sourceFile));
}
+ isPcBasedDebugInfo = false;
break;
}
default: {
assert head >= 0x0a && head <= 0xff;
- events.add(dexItemFactory.createDefault(head));
+ Default event = dexItemFactory.createDefault(head);
+ events.add(event);
+ if (isPcBasedDebugInfo) {
+ if (events.size() == 1) {
+ isPcBasedDebugInfo = event.equals(dexItemFactory.zeroChangeDefaultEvent);
+ } else {
+ isPcBasedDebugInfo = event.equals(dexItemFactory.oneChangeDefaultEvent);
+ }
+ }
}
}
}
- return new DexDebugInfo(start, parameters, events.toArray(DexDebugEvent.EMPTY_ARRAY));
+ return isPcBasedDebugInfo
+ ? new PcBasedDebugInfo(parametersSize, events.size() - 1)
+ : new EventBasedDebugInfo(start, parameters, events.toArray(DexDebugEvent.EMPTY_ARRAY));
}
private static class MemberAnnotationIterator<R extends DexMember<?, R>, T extends DexItem> {
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 84b245c..ebf21c6 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
-import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexDebugInfoForWriting;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedArray;
@@ -184,7 +183,7 @@
} else {
// Ensure deterministic ordering of debug info by sorting consistent with the code objects.
layout.setDebugInfosOffset(dest.align(1));
- Set<DexDebugInfo> seen = new HashSet<>(mixedSectionOffsets.getDebugInfos().size());
+ Set<DexDebugInfoForWriting> seen = new HashSet<>(mixedSectionOffsets.getDebugInfos().size());
for (ProgramDexCode code : codes) {
DexDebugInfoForWriting info = code.getCode().getDebugInfoForWriting();
if (info != null && seen.add(info)) {
@@ -480,7 +479,7 @@
dest.putInt(mixedSectionOffsets.getOffsetFor(staticFieldValues.get(clazz)));
}
- private void writeDebugItem(DexDebugInfo debugInfo, GraphLens graphLens) {
+ private void writeDebugItem(DexDebugInfoForWriting debugInfo, GraphLens graphLens) {
mixedSectionOffsets.setOffsetFor(debugInfo, dest.position());
dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping, graphLens).generate());
}
@@ -1070,7 +1069,7 @@
private static final int NOT_KNOWN = -2;
private final Reference2IntMap<DexEncodedMethod> codes = createReference2IntMap();
- private final Object2IntMap<DexDebugInfo> debugInfos = createObject2IntMap();
+ private final Object2IntMap<DexDebugInfoForWriting> debugInfos = createObject2IntMap();
private final Object2IntMap<DexTypeList> typeLists = createObject2IntMap();
private final Reference2IntMap<DexString> stringData = createReference2IntMap();
private final Object2IntMap<DexAnnotation> annotations = createObject2IntMap();
@@ -1149,7 +1148,7 @@
}
@Override
- public boolean add(DexDebugInfo debugInfo) {
+ public boolean add(DexDebugInfoForWriting debugInfo) {
return add(debugInfos, debugInfo);
}
@@ -1190,7 +1189,7 @@
return codes.keySet();
}
- public Collection<DexDebugInfo> getDebugInfos() {
+ public Collection<DexDebugInfoForWriting> getDebugInfos() {
return debugInfos.keySet();
}
@@ -1263,7 +1262,7 @@
return lookup(encodedArray, encodedArrays);
}
- public int getOffsetFor(DexDebugInfo debugInfo) {
+ public int getOffsetFor(DexDebugInfoForWriting debugInfo) {
return lookup(debugInfo, debugInfos);
}
@@ -1311,7 +1310,7 @@
assert old <= NOT_SET;
}
- void setOffsetFor(DexDebugInfo debugInfo, int offset) {
+ void setOffsetFor(DexDebugInfoForWriting debugInfo, int offset) {
setOffsetFor(debugInfo, offset, debugInfos);
}
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index 990d545..fc3784f 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
@@ -93,8 +94,8 @@
private final DexString firstJumboString;
private final DexItemFactory factory;
private final Map<Instruction, List<Instruction>> instructionTargets = new IdentityHashMap<>();
- private final Int2ReferenceMap<Instruction> debugEventTargets
- = new Int2ReferenceOpenHashMap<>();
+ private EventBasedDebugInfo debugEventBasedInfo = null;
+ private final Int2ReferenceMap<Instruction> debugEventTargets = new Int2ReferenceOpenHashMap<>();
private final Map<Instruction, Instruction> payloadToSwitch = new IdentityHashMap<>();
private final Map<Try, TryTargets> tryTargets = new IdentityHashMap<>();
private final Int2ReferenceMap<Instruction> tryRangeStartAndEndTargets
@@ -216,11 +217,12 @@
private DexDebugInfo rewriteDebugInfoOffsets() {
DexCode code = method.getCode().asDexCode();
- if (debugEventTargets.size() != 0) {
+ if (!debugEventTargets.isEmpty()) {
+ assert debugEventBasedInfo != null;
int lastOriginalOffset = 0;
int lastNewOffset = 0;
List<DexDebugEvent> events = new ArrayList<>();
- for (DexDebugEvent event : code.getDebugInfo().events) {
+ for (DexDebugEvent event : debugEventBasedInfo.events) {
if (event instanceof AdvancePC) {
AdvancePC advance = (AdvancePC) event;
lastOriginalOffset += advance.delta;
@@ -240,9 +242,9 @@
events.add(event);
}
}
- return new DexDebugInfo(
- code.getDebugInfo().startLine,
- code.getDebugInfo().parameters,
+ return new EventBasedDebugInfo(
+ debugEventBasedInfo.startLine,
+ debugEventBasedInfo.parameters,
events.toArray(DexDebugEvent.EMPTY_ARRAY));
}
return code.getDebugInfo();
@@ -479,23 +481,30 @@
}
private void recordDebugEventTargets(Int2ReferenceMap<Instruction> offsetToInstruction) {
- DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo();
- if (debugInfo != null) {
- int address = 0;
- for (DexDebugEvent event : debugInfo.events) {
- if (event instanceof AdvancePC) {
- AdvancePC advance = (AdvancePC) event;
- address += advance.delta;
- Instruction target = offsetToInstruction.get(address);
- assert target != null;
- debugEventTargets.put(address, target);
- } else if (event instanceof Default) {
- Default defaultEvent = (Default) event;
- address += defaultEvent.getPCDelta();
- Instruction target = offsetToInstruction.get(address);
- assert target != null;
- debugEventTargets.put(address, target);
- }
+ // TODO(b/213411850): Merging pc based D8 builds will map out of PC for any jumbo processed
+ // method. Instead we should rather retain the PC encoding by bumping the max-pc and recording
+ // the line number translation. We actually need to do so to support merging with native PC
+ // support as in that case we can't reflect the change in the line table, only in the mapping.
+ EventBasedDebugInfo eventBasedInfo =
+ DexDebugInfo.convertToEventBased(method.getCode().asDexCode(), factory);
+ if (eventBasedInfo == null) {
+ return;
+ }
+ debugEventBasedInfo = eventBasedInfo;
+ int address = 0;
+ for (DexDebugEvent event : eventBasedInfo.events) {
+ if (event instanceof AdvancePC) {
+ AdvancePC advance = (AdvancePC) event;
+ address += advance.delta;
+ Instruction target = offsetToInstruction.get(address);
+ assert target != null;
+ debugEventTargets.put(address, target);
+ } else if (event instanceof Default) {
+ Default defaultEvent = (Default) event;
+ address += defaultEvent.getPCDelta();
+ Instruction target = offsetToInstruction.get(address);
+ assert target != null;
+ debugEventTargets.put(address, target);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
index e082a31..f0c4c06 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfoForWriting;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
@@ -74,11 +74,11 @@
/**
* Adds the given debug info to the collection.
*
- * Does not add any dependencies.
+ * <p>Does not add any dependencies.
*
* @return true if the item was not added before
*/
- public abstract boolean add(DexDebugInfo dexDebugInfo);
+ public abstract boolean add(DexDebugInfoForWriting dexDebugInfo);
/**
* Adds the given type list to the collection.
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 7c7824d..07ebb29 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -803,7 +803,7 @@
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
List<CfInstruction> newInstructions = new ArrayList<>(instructions.size() + 2);
CfLabel firstLabel;
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index be9507d..e727373 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -149,7 +149,7 @@
return true;
}
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
throw new Unreachable();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 703977e..11d3e17 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
import com.android.tools.r8.ir.code.IRCode;
@@ -27,6 +28,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.structural.Equatable;
import com.android.tools.r8.utils.structural.HashCodeVisitor;
@@ -170,7 +172,7 @@
}
}
- public DexCode withoutThisParameter() {
+ public DexCode withoutThisParameter(DexItemFactory factory) {
// Note that we assume the original code has a register associated with 'this'
// argument of the (former) instance method. We also assume (but do not check)
// that 'this' register is never used, so when we decrease incoming register size
@@ -184,7 +186,7 @@
instructions,
tries,
handlers,
- debugInfoWithoutFirstParameter());
+ debugInfoWithoutFirstParameter(factory));
}
@Override
@@ -229,16 +231,17 @@
}
public DexDebugInfo debugInfoWithFakeThisParameter(DexItemFactory factory) {
- if (debugInfo == null) {
- return null;
+ EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
+ if (eventBasedInfo == null) {
+ return eventBasedInfo;
}
// User code may already have variables named '_*this'. Use one more than the largest number of
// underscores present as a prefix to 'this'.
int largestPrefix = 0;
- for (DexString parameter : debugInfo.parameters) {
+ for (DexString parameter : eventBasedInfo.parameters) {
largestPrefix = Integer.max(largestPrefix, getLargestPrefix(factory, parameter));
}
- for (DexDebugEvent event : debugInfo.events) {
+ for (DexDebugEvent event : eventBasedInfo.events) {
if (event instanceof DexDebugEvent.StartLocal) {
DexString name = ((StartLocal) event).name;
largestPrefix = Integer.max(largestPrefix, getLargestPrefix(factory, name));
@@ -246,15 +249,15 @@
}
String fakeThisName = Strings.repeat(FAKE_THIS_PREFIX, largestPrefix + 1) + FAKE_THIS_SUFFIX;
- DexString[] parameters = debugInfo.parameters;
+ DexString[] parameters = eventBasedInfo.parameters;
DexString[] newParameters = new DexString[parameters.length + 1];
newParameters[0] = factory.createString(fakeThisName);
System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
- return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
+ return new EventBasedDebugInfo(eventBasedInfo.startLine, newParameters, eventBasedInfo.events);
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
return new DexCode(
registerSize,
incomingRegisterSize,
@@ -262,24 +265,25 @@
instructions,
tries,
handlers,
- debugInfoAsInlining(caller, callee));
+ debugInfoAsInlining(caller, callee, factory));
}
- private DexDebugInfo debugInfoAsInlining(DexMethod caller, DexMethod callee) {
+ private DexDebugInfo debugInfoAsInlining(
+ DexMethod caller, DexMethod callee, DexItemFactory factory) {
Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
- if (debugInfo == null) {
+ EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
+ if (eventBasedInfo == null) {
// If the method has no debug info we generate a preamble position to denote the inlining.
// This is consistent with the building IR for inlining which will always ensure the method
// has a position.
- return new DexDebugInfo(
+ return new EventBasedDebugInfo(
0,
new DexString[callee.getArity()],
new DexDebugEvent[] {
- new DexDebugEvent.SetInlineFrame(callee, callerPosition),
- DexDebugEvent.ZERO_CHANGE_DEFAULT_EVENT
+ new DexDebugEvent.SetInlineFrame(callee, callerPosition), factory.zeroChangeDefaultEvent
});
}
- DexDebugEvent[] oldEvents = debugInfo.events;
+ DexDebugEvent[] oldEvents = eventBasedInfo.events;
DexDebugEvent[] newEvents = new DexDebugEvent[oldEvents.length + 1];
int i = 0;
newEvents[i++] = new DexDebugEvent.SetInlineFrame(callee, callerPosition);
@@ -296,7 +300,7 @@
newEvents[i++] = event;
}
}
- return new DexDebugInfo(debugInfo.startLine, debugInfo.parameters, newEvents);
+ return new EventBasedDebugInfo(eventBasedInfo.startLine, eventBasedInfo.parameters, newEvents);
}
public static int getLargestPrefix(DexItemFactory factory, DexString name) {
@@ -311,17 +315,18 @@
return 0;
}
- public DexDebugInfo debugInfoWithoutFirstParameter() {
- if (debugInfo == null) {
- return null;
+ public DexDebugInfo debugInfoWithoutFirstParameter(DexItemFactory factory) {
+ EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
+ if (eventBasedInfo == null) {
+ return eventBasedInfo;
}
- DexString[] parameters = debugInfo.parameters;
+ DexString[] parameters = eventBasedInfo.parameters;
if(parameters.length == 0) {
- return debugInfo;
+ return eventBasedInfo;
}
DexString[] newParameters = new DexString[parameters.length - 1];
System.arraycopy(parameters, 1, newParameters, 0, parameters.length - 1);
- return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
+ return new EventBasedDebugInfo(eventBasedInfo.startLine, newParameters, eventBasedInfo.events);
}
@Override
@@ -362,7 +367,8 @@
this,
method,
appView.graphLens().getOriginalMethodSignature(method.getReference()),
- null);
+ null,
+ appView.dexItemFactory());
return IRBuilder.create(method, appView, source, origin).build(method);
}
@@ -381,7 +387,8 @@
this,
method,
appView.graphLens().getOriginalMethodSignature(method.getReference()),
- callerPosition);
+ callerPosition,
+ appView.dexItemFactory());
return IRBuilder.createForInlining(
method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
.build(context);
@@ -443,13 +450,15 @@
DexDebugEntry debugInfo = null;
Iterator<DexDebugEntry> debugInfoIterator = Collections.emptyIterator();
- if (getDebugInfo() != null && method != null) {
+ boolean isPcBasedInfo = getDebugInfo() != null && getDebugInfo().isPcBasedInfo();
+ if (!isPcBasedInfo && getDebugInfo() != null && method != null) {
debugInfoIterator = new DexDebugEntryBuilder(method, new DexItemFactory()).build().iterator();
debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
}
int instructionNumber = 0;
Map<Integer, DebugLocalInfo> locals = Collections.emptyMap();
for (Instruction insn : instructions) {
+ debugInfo = advanceToOffset(insn.getOffset() - 1, debugInfo, debugInfoIterator);
while (debugInfo != null && debugInfo.address == insn.getOffset()) {
if (debugInfo.lineEntry || !locals.equals(debugInfo.locals)) {
builder.append(" ").append(debugInfo.toString(false)).append("\n");
@@ -467,8 +476,16 @@
}
builder.append('\n');
}
- if (debugInfoIterator.hasNext()) {
- throw new Unreachable("Could not print all debug information.");
+ if (isPcBasedInfo) {
+ builder.append(getDebugInfo()).append("\n");
+ } else if (debugInfoIterator.hasNext()) {
+ Instruction lastInstruction = ArrayUtils.last(instructions);
+ debugInfo = advanceToOffset(lastInstruction.getOffset(), debugInfo, debugInfoIterator);
+ if (debugInfo != null) {
+ throw new Unreachable("Could not print all debug information.");
+ } else {
+ builder.append("(has debug events past last pc)\n");
+ }
}
if (tries.length > 0) {
builder.append("Tries (numbers are offsets)\n");
@@ -488,6 +505,14 @@
return builder.toString();
}
+ DexDebugEntry advanceToOffset(
+ int offset, DexDebugEntry current, Iterator<DexDebugEntry> iterator) {
+ while (current != null && current.address <= offset) {
+ current = iterator.hasNext() ? iterator.next() : null;
+ }
+ return current;
+ }
+
public String toSmaliString(ClassNameMapper naming) {
StringBuilder builder = new StringBuilder();
// Find labeled targets.
@@ -566,18 +591,15 @@
if (debugInfo != null) {
getDebugInfoForWriting().collectIndexedItems(indexedItems, graphLens);
}
- for (TryHandler handler : handlers) {
- handler.collectIndexedItems(indexedItems, graphLens);
- }
+ for (TryHandler handler : handlers) {
+ handler.collectIndexedItems(indexedItems, graphLens);
+ }
}
@Override
public DexDebugInfoForWriting getDebugInfoForWriting() {
- if (debugInfo == null) {
- return null;
- }
if (debugInfoForWriting == null) {
- debugInfoForWriting = DexDebugInfoForWriting.create(debugInfo);
+ debugInfoForWriting = DexDebugInfo.convertToWritable(debugInfo);
}
return debugInfoForWriting;
}
@@ -619,10 +641,6 @@
}
}
- public boolean usesExceptionHandling() {
- return tries.length != 0;
- }
-
@Override
public void collectMixedSectionItems(MixedSectionCollection mixedItems) {
if (debugInfo != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index ce338d6..d8b6a9b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.DexDebugEvent.SetOutlineCallerFrame;
import com.android.tools.r8.graph.DexDebugEvent.SetOutlineFrame;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.ir.code.ValueType;
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
@@ -67,11 +68,11 @@
public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
assert method != null && method.getReference() != null;
this.method = method.getReference();
- positionState =
- new DexDebugPositionState(
- method.getCode().asDexCode().getDebugInfo().startLine, method.getReference());
DexCode code = method.getCode().asDexCode();
- DexDebugInfo info = code.getDebugInfo();
+ EventBasedDebugInfo info = code.getDebugInfo().asEventBasedInfo();
+ // Only event based debug info supports conversion to entries.
+ assert info != null;
+ positionState = new DexDebugPositionState(info.startLine, method.getReference());
int argumentRegister = code.registerSize - code.incomingRegisterSize;
if (!method.accessFlags.isStatic()) {
DexString name = factory.thisName;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index e4994c0..60cc65a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -26,8 +26,6 @@
public static final DexDebugEvent[] EMPTY_ARRAY = {};
- public static final DexDebugEvent.Default ZERO_CHANGE_DEFAULT_EVENT = Default.create(0, 0);
-
public void collectIndexedItems(IndexedItemCollection collection, GraphLens graphLens) {
// Empty by default.
}
@@ -746,12 +744,14 @@
this.value = value;
}
- public static int computeSpecialOpcode(int lineDelta, int pcDelta) {
+ // Use DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary instead.
+ static int computeSpecialOpcode(int lineDelta, int pcDelta) {
return Constants.DBG_FIRST_SPECIAL
+ (lineDelta - Constants.DBG_LINE_BASE)
+ Constants.DBG_LINE_RANGE * pcDelta;
}
+ // Use DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary instead.
public static Default create(int lineDelta, int pcDelta) {
return new Default(computeSpecialOpcode(lineDelta, pcDelta));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 71822f6..dc90361 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.DebugLocalsChange;
import com.android.tools.r8.ir.code.IRCode;
@@ -120,7 +121,7 @@
params[i] = (local == null || local.signature != null) ? null : local.name;
}
}
- return new DexDebugInfo(startLine, params, events.toArray(DexDebugEvent.EMPTY_ARRAY));
+ return new EventBasedDebugInfo(startLine, params, events.toArray(DexDebugEvent.EMPTY_ARRAY));
}
private void updateBlockEntry(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 75588f6..0f27bda 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -3,92 +3,368 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DebugBytecodeWriter;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.LebUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.Equatable;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
-public class DexDebugInfo extends CachedHashValueDexItem implements StructuralItem<DexDebugInfo> {
+public abstract class DexDebugInfo extends CachedHashValueDexItem
+ implements StructuralItem<DexDebugInfo> {
- public final int startLine;
- public final DexString[] parameters;
- public DexDebugEvent[] events;
-
- private static void specify(StructuralSpecification<DexDebugInfo, ?> spec) {
- spec.withInt(d -> d.startLine)
- .withItemArrayAllowingNullMembers(d -> d.parameters)
- .withItemArray(d -> d.events);
+ private enum DebugInfoKind {
+ EVENT_BASED,
+ PC_BASED
}
- public DexDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) {
- assert startLine >= 0;
- this.startLine = startLine;
- this.parameters = parameters;
- this.events = events;
- // This call to hashCode is just an optimization to speedup equality when
- // canonicalizing DexDebugInfo objects inside a synchronize method.
- hashCode();
+ abstract DebugInfoKind getKind();
+
+ abstract int internalAcceptCompareTo(DexDebugInfo other, CompareToVisitor visitor);
+
+ public abstract int getStartLine();
+
+ public abstract int getParameterCount();
+
+ public boolean isEventBasedInfo() {
+ return getKind() == DebugInfoKind.EVENT_BASED;
+ }
+
+ public boolean isPcBasedInfo() {
+ return getKind() == DebugInfoKind.PC_BASED;
+ }
+
+ public EventBasedDebugInfo asEventBasedInfo() {
+ return null;
+ }
+
+ public PcBasedDebugInfo asPcBasedInfo() {
+ return null;
}
@Override
- public DexDebugInfo self() {
- return this;
- }
+ abstract void collectMixedSectionItems(MixedSectionCollection collection);
+
+ @Override
+ public abstract DexDebugInfo self();
@Override
public StructuralMapping<DexDebugInfo> getStructuralMapping() {
- return DexDebugInfo::specify;
+ throw new Unreachable();
}
- public List<DexDebugEntry> computeEntries(DexMethod method) {
- DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine, method);
- for (DexDebugEvent event : events) {
- event.accept(builder);
+ @Override
+ public abstract void acceptHashing(HashingVisitor visitor);
+
+ @Override
+ public int acceptCompareTo(DexDebugInfo other, CompareToVisitor visitor) {
+ int diff = visitor.visitInt(getKind().ordinal(), other.getKind().ordinal());
+ if (diff != 0) {
+ return diff;
}
- return builder.build();
+ return internalAcceptCompareTo(other, visitor);
}
@Override
- public int computeHashCode() {
- return startLine
- + Arrays.hashCode(parameters) * 7
- + Arrays.hashCode(events) * 13;
- }
-
- @Override
- public final boolean computeEquals(Object other) {
+ protected final boolean computeEquals(Object other) {
return Equatable.equalsImpl(this, other);
}
- public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
- for (DexString parameter : parameters) {
- if (parameter != null) {
- parameter.collectIndexedItems(indexedItems);
+ public static class PcBasedDebugInfo extends DexDebugInfo implements DexDebugInfoForWriting {
+ private static final int START_LINE = 0;
+ private final int parameterCount;
+ private final int maxPc;
+
+ private static void specify(StructuralSpecification<PcBasedDebugInfo, ?> spec) {
+ spec.withInt(d -> d.parameterCount).withInt(d -> d.maxPc);
+ }
+
+ public PcBasedDebugInfo(int parameterCount, int maxPc) {
+ this.parameterCount = parameterCount;
+ this.maxPc = maxPc;
+ }
+
+ @Override
+ public int getStartLine() {
+ return START_LINE;
+ }
+
+ @Override
+ public int getParameterCount() {
+ return parameterCount;
+ }
+
+ @Override
+ public DexDebugInfo self() {
+ return this;
+ }
+
+ @Override
+ public PcBasedDebugInfo asPcBasedInfo() {
+ return this;
+ }
+
+ @Override
+ DebugInfoKind getKind() {
+ return DebugInfoKind.PC_BASED;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ return Objects.hash(parameterCount, maxPc);
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visit(this, PcBasedDebugInfo::specify);
+ }
+
+ @Override
+ int internalAcceptCompareTo(DexDebugInfo other, CompareToVisitor visitor) {
+ assert other.isPcBasedInfo();
+ return visitor.visit(this, other.asPcBasedInfo(), PcBasedDebugInfo::specify);
+ }
+
+ @Override
+ public void collectMixedSectionItems(MixedSectionCollection collection) {
+ collection.add(this);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
+ // No indexed items to collect.
+ }
+
+ @Override
+ public int estimatedWriteSize() {
+ return LebUtils.sizeAsUleb128(START_LINE)
+ + LebUtils.sizeAsUleb128(parameterCount)
+ + parameterCount * LebUtils.sizeAsUleb128(0)
+ + 1
+ + maxPc
+ + 1;
+ }
+
+ @Override
+ public void write(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+ writer.putUleb128(START_LINE);
+ writer.putUleb128(parameterCount);
+ for (int i = 0; i < parameterCount; i++) {
+ writer.putString(null);
+ }
+ mapping.dexItemFactory().zeroChangeDefaultEvent.writeOn(writer, mapping, graphLens);
+ for (int i = 0; i < maxPc; i++) {
+ mapping.dexItemFactory().oneChangeDefaultEvent.writeOn(writer, mapping, graphLens);
+ }
+ writer.putByte(Constants.DBG_END_SEQUENCE);
+ }
+
+ @Override
+ public String toString() {
+ return "PcBasedDebugInfo (params: "
+ + parameterCount
+ + ", max-pc: "
+ + StringUtils.hexString(maxPc, 2)
+ + ")";
+ }
+ }
+
+ public static class EventBasedDebugInfo extends DexDebugInfo {
+
+ public final int startLine;
+ public final DexString[] parameters;
+ public DexDebugEvent[] events;
+
+ private static void specify(StructuralSpecification<EventBasedDebugInfo, ?> spec) {
+ spec.withInt(d -> d.startLine)
+ .withItemArrayAllowingNullMembers(d -> d.parameters)
+ .withItemArray(d -> d.events);
+ }
+
+ public EventBasedDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) {
+ assert startLine >= 0;
+ this.startLine = startLine;
+ this.parameters = parameters;
+ this.events = events;
+ }
+
+ @Override
+ public DexDebugInfo self() {
+ return this;
+ }
+
+ @Override
+ public EventBasedDebugInfo asEventBasedInfo() {
+ return this;
+ }
+
+ @Override
+ DebugInfoKind getKind() {
+ return DebugInfoKind.EVENT_BASED;
+ }
+
+ @Override
+ public int getStartLine() {
+ return startLine;
+ }
+
+ @Override
+ public int getParameterCount() {
+ return parameters.length;
+ }
+
+ public List<DexDebugEntry> computeEntries(DexMethod method) {
+ DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine, method);
+ for (DexDebugEvent event : events) {
+ event.accept(builder);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public int computeHashCode() {
+ return startLine + Arrays.hashCode(parameters) * 7 + Arrays.hashCode(events) * 13;
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visit(this, EventBasedDebugInfo::specify);
+ }
+
+ @Override
+ int internalAcceptCompareTo(DexDebugInfo other, CompareToVisitor visitor) {
+ assert other.isEventBasedInfo();
+ return visitor.visit(this, other.asEventBasedInfo(), EventBasedDebugInfo::specify);
+ }
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
+ for (DexString parameter : parameters) {
+ if (parameter != null) {
+ parameter.collectIndexedItems(indexedItems);
+ }
+ }
+ for (DexDebugEvent event : events) {
+ event.collectIndexedItems(indexedItems, graphLens);
}
}
- for (DexDebugEvent event : events) {
- event.collectIndexedItems(indexedItems, graphLens);
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection collection) {
+ // Only writable info should be iterated for collection.
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DebugInfo (line " + startLine + ") events: [\n");
+ for (DexDebugEvent event : events) {
+ builder.append(" ").append(event).append("\n");
+ }
+ builder.append(" END_SEQUENCE\n");
+ builder.append("]\n");
+ return builder.toString();
}
}
- @Override
- void collectMixedSectionItems(MixedSectionCollection collection) {
- collection.add(this);
+ public static EventBasedDebugInfo convertToEventBased(DexCode code, DexItemFactory factory) {
+ if (code.getDebugInfo() == null) {
+ return null;
+ }
+ if (code.getDebugInfo().isEventBasedInfo()) {
+ return code.getDebugInfo().asEventBasedInfo();
+ }
+ assert code.getDebugInfo().isPcBasedInfo();
+ PcBasedDebugInfo pcBasedDebugInfo = code.getDebugInfo().asPcBasedInfo();
+ // Generate a line event at each throwing instruction.
+ List<DexDebugEvent> events = new ArrayList<>(code.instructions.length + 1);
+ events.add(factory.zeroChangeDefaultEvent);
+ int pc = 0;
+ int delta = 0;
+ for (Instruction instruction : code.instructions) {
+ delta += instruction.getSize();
+ if (instruction.canThrow()) {
+ DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory);
+ pc += delta;
+ delta = 0;
+ }
+ }
+ assert pc + delta - ArrayUtils.last(code.instructions).getSize() <= pcBasedDebugInfo.maxPc;
+ return new EventBasedDebugInfo(
+ PcBasedDebugInfo.START_LINE,
+ new DexString[pcBasedDebugInfo.getParameterCount()],
+ events.toArray(DexDebugEvent.EMPTY_ARRAY));
}
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("DebugInfo (line " + startLine + ") events: [\n");
- for (DexDebugEvent event : events) {
- builder.append(" ").append(event).append("\n");
+ public static DexDebugInfoForWriting convertToWritable(DexDebugInfo debugInfo) {
+ if (debugInfo == null) {
+ return null;
}
- builder.append(" END_SEQUENCE\n");
- builder.append("]\n");
- return builder.toString();
+ if (debugInfo.isPcBasedInfo()) {
+ return debugInfo.asPcBasedInfo();
+ }
+ EventBasedDebugInfo eventBasedInfo = debugInfo.asEventBasedInfo();
+ DexDebugEvent[] writableEvents =
+ ArrayUtils.filter(
+ eventBasedInfo.events, DexDebugEvent::isWritableEvent, DexDebugEvent.EMPTY_ARRAY);
+ return new WritableEventBasedDebugInfo(
+ eventBasedInfo.startLine, eventBasedInfo.parameters, writableEvents);
+ }
+
+ private static class WritableEventBasedDebugInfo extends EventBasedDebugInfo
+ implements DexDebugInfoForWriting {
+
+ private WritableEventBasedDebugInfo(
+ int startLine, DexString[] parameters, DexDebugEvent[] writableEvents) {
+ super(startLine, parameters, writableEvents);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
+ super.collectIndexedItems(indexedItems, graphLens);
+ }
+
+ @Override
+ public void collectMixedSectionItems(MixedSectionCollection collection) {
+ collection.add(this);
+ }
+
+ @Override
+ public int estimatedWriteSize() {
+ return LebUtils.sizeAsUleb128(startLine)
+ + LebUtils.sizeAsUleb128(parameters.length)
+ // Estimate 4 bytes per parameter pointer.
+ + parameters.length * 4
+ + events.length
+ + 1;
+ }
+
+ @Override
+ public void write(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+ writer.putUleb128(startLine);
+ writer.putUleb128(parameters.length);
+ for (DexString name : parameters) {
+ writer.putString(name);
+ }
+ for (DexDebugEvent event : events) {
+ event.writeOn(writer, mapping, graphLens);
+ }
+ writer.putByte(Constants.DBG_END_SEQUENCE);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
index 32d3126..8d41561 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.graph;
-public class DexDebugInfoForSingleLineMethod extends DexDebugInfo {
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
+
+public class DexDebugInfoForSingleLineMethod extends EventBasedDebugInfo {
private static final DexDebugInfoForSingleLineMethod INSTANCE =
new DexDebugInfoForSingleLineMethod(0, DexString.EMPTY_ARRAY, DexDebugEvent.EMPTY_ARRAY);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
index ddb4efc..5f94db9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
@@ -4,36 +4,18 @@
package com.android.tools.r8.graph;
-/**
- * Wraps DexDebugInfo to make comparison and hashcode not consider
- * the SetInlineFrames
- */
-public class DexDebugInfoForWriting extends DexDebugInfo {
+import com.android.tools.r8.dex.DebugBytecodeWriter;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
- private DexDebugInfoForWriting(int startLine, DexString[] parameters, DexDebugEvent[] events) {
- super(startLine, parameters, events);
- }
+/** Interface to guarantee that the info only contains writable events. */
+public interface DexDebugInfoForWriting {
- public static DexDebugInfoForWriting create(DexDebugInfo debugInfo) {
- assert debugInfo != null;
- int nonWritableEvents = 0;
- for (DexDebugEvent event : debugInfo.events) {
- if (!event.isWritableEvent()) {
- nonWritableEvents++;
- }
- }
- DexDebugEvent[] writableEvents;
- if (nonWritableEvents == 0) {
- writableEvents = debugInfo.events;
- } else {
- writableEvents = new DexDebugEvent[debugInfo.events.length - nonWritableEvents];
- int i = 0;
- for (DexDebugEvent event : debugInfo.events) {
- if (event.isWritableEvent()) {
- writableEvents[i++] = event;
- }
- }
- }
- return new DexDebugInfoForWriting(debugInfo.startLine, debugInfo.parameters, writableEvents);
- }
+ void collectMixedSectionItems(MixedSectionCollection collection);
+
+ void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens);
+
+ int estimatedWriteSize();
+
+ void write(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 289f255..865a536 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -992,9 +992,9 @@
public static void setDebugInfoWithFakeThisParameter(Code code, int arity, AppView<?> appView) {
if (code.isDexCode()) {
DexCode dexCode = code.asDexCode();
- dexCode.setDebugInfo(dexCode.debugInfoWithFakeThisParameter(appView.dexItemFactory()));
- assert (dexCode.getDebugInfo() == null)
- || (arity == dexCode.getDebugInfo().parameters.length);
+ DexDebugInfo newDebugInfo = dexCode.debugInfoWithFakeThisParameter(appView.dexItemFactory());
+ assert (newDebugInfo == null) || (arity == newDebugInfo.getParameterCount());
+ dexCode.setDebugInfo(newDebugInfo);
} else {
assert code.isCfCode();
CfCode cfCode = code.asCfCode();
@@ -1222,7 +1222,7 @@
Builder builder =
builder(this)
.promoteToStatic()
- .withoutThisParameter()
+ .withoutThisParameter(appView.dexItemFactory())
.fixupOptimizationInfo(appView, prototypeChanges.createMethodOptimizationInfoFixer())
.setGenericSignature(MethodTypeSignature.noSignature());
DexEncodedMethod method = builder.build();
@@ -1524,10 +1524,10 @@
return this;
}
- public Builder withoutThisParameter() {
+ public Builder withoutThisParameter(DexItemFactory factory) {
assert code != null;
if (code.isDexCode()) {
- code = code.asDexCode().withoutThisParameter();
+ code = code.asDexCode().withoutThisParameter(factory);
} else {
throw new Unreachable("Code " + code.getClass().getSimpleName() + " is not supported.");
}
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 c064992..5421ba6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -99,6 +99,8 @@
private final Map<DexString, SetFile> setFiles = new HashMap<>();
private final SetOutlineFrame setOutlineFrame = new SetOutlineFrame();
private final Map<SetInlineFrame, SetInlineFrame> setInlineFrames = new HashMap<>();
+ public final DexDebugEvent.Default zeroChangeDefaultEvent = createDefault(0, 0);
+ public final DexDebugEvent.Default oneChangeDefaultEvent = createDefault(1, 1);
// ReferenceTypeElement canonicalization.
private final ConcurrentHashMap<DexType, ReferenceTypeElement> referenceTypes =
@@ -2744,6 +2746,10 @@
}
}
+ public Default createDefault(int lineDelta, int pcDelta) {
+ return createDefault(Default.create(lineDelta, pcDelta).value);
+ }
+
public EndLocal createEndLocal(int registerNum) {
synchronized (endLocals) {
return endLocals.computeIfAbsent(registerNum, EndLocal::new);
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index ae16c5a..457a6f7 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -170,8 +170,8 @@
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
- return asCfCode().getCodeAsInlining(caller, callee);
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ return asCfCode().getCodeAsInlining(caller, callee, factory);
}
public static class DebugParsingOptions {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index f2c1d8d..6dec27c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEntry;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -77,11 +78,15 @@
private final DexMethod originalMethod;
public DexSourceCode(
- DexCode code, ProgramMethod method, DexMethod originalMethod, Position callerPosition) {
+ DexCode code,
+ ProgramMethod method,
+ DexMethod originalMethod,
+ Position callerPosition,
+ DexItemFactory factory) {
this.code = code;
this.method = method;
this.originalMethod = originalMethod;
- DexDebugInfo info = code.getDebugInfo();
+ EventBasedDebugInfo info = DexDebugInfo.convertToEventBased(code, factory);
if (info != null) {
debugEntries = info.computeEntries(originalMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index de940f8..71db541 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -245,6 +245,7 @@
private void finalizeConstantDynamicDesugaring(Consumer<ProgramMethod> needsProcessing) {
for (ConstantDynamicClass constantDynamicClass : synthesizedConstantDynamicClasses) {
+ constantDynamicClass.rewriteBootstrapMethodSignatureIfNeeded();
constantDynamicClass.getConstantDynamicProgramClass().forEachProgramMethod(needsProcessing);
}
synthesizedConstantDynamicClasses.clear();
@@ -408,6 +409,7 @@
public void finalizeDesugaring() {
finalizeInvokeSpecialDesugaring();
finalizeLambdaDesugaring();
+ // TODO(b/210485236): Finalize constant dynamic desugaring by rewriting signature if needed.
}
private void finalizeInvokeSpecialDesugaring() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index fb848df..a332828 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -41,6 +42,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
@@ -51,10 +53,13 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import com.google.common.collect.ImmutableList;
@@ -80,6 +85,9 @@
public final DexField constantValueField;
private final DexMethod getConstMethod;
private final Behaviour behaviour;
+ private DexMethod bootstrapMethodReference;
+ private DexMethod finalBootstrapMethodReference;
+ private boolean isFinalBootstrapMethodReferenceOnInterface;
// Considered final but is set after due to circularity in allocation.
private DexProgramClass clazz = null;
@@ -108,19 +116,49 @@
factory.createString("get"));
DexMethodHandle bootstrapMethodHandle = reference.getBootstrapMethod();
- DexMethod bootstrapMethodReference = bootstrapMethodHandle.asMethod();
+ bootstrapMethodReference = bootstrapMethodHandle.asMethod();
MethodResolutionResult resolution =
appView
.appInfoForDesugaring()
.resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface);
if (resolution.isSingleResolution()
&& resolution.asSingleResolution().getResolvedMethod().isStatic()) {
- // Ensure that the bootstrap method is accessible from the generated class.
SingleResolutionResult result = resolution.asSingleResolution();
- MethodAccessFlags flags = result.getResolvedMethod().getAccessFlags();
- flags.unsetPrivate();
- flags.setPublic();
+ if (bootstrapMethodHandle.isInterface
+ && appView.options().isInterfaceMethodDesugaringEnabled()) {
+ bootstrapMethodReference =
+ bootstrapMethodReference.withHolder(
+ InterfaceDesugaringSyntheticHelper.getCompanionClassType(
+ bootstrapMethodReference.getHolderType(), factory),
+ factory);
+ isFinalBootstrapMethodReferenceOnInterface = false;
+ } else {
+ assert bootstrapMethodReference.getHolderType() == resolution.getResolvedHolder().getType();
+ isFinalBootstrapMethodReferenceOnInterface = bootstrapMethodHandle.isInterface;
+ }
+ if (shouldRewriteBootstrapMethodSignature()) {
+ // The bootstrap method will have its signature modified to have type Object as its first
+ // argument.
+ this.finalBootstrapMethodReference =
+ factory.createMethod(
+ bootstrapMethodReference.getHolderType(),
+ factory.createProto(
+ bootstrapMethodReference.getReturnType(),
+ factory.objectType,
+ factory.stringType,
+ factory.classType),
+ bootstrapMethodReference.getName());
+ } else {
+ this.finalBootstrapMethodReference = bootstrapMethodReference;
+ // Ensure that the bootstrap method is accessible from the generated class.
+ DexEncodedMethod bootstrapMethodImpl = result.getResolvedMethod();
+ MethodAccessFlags flags = bootstrapMethodImpl.getAccessFlags();
+ flags.unsetPrivate();
+ flags.setPublic();
+ }
+
behaviour = CACHE_CONSTANT;
+
synthesizeConstantDynamicClass(builder);
} else {
// Unconditionally throw as the RI.
@@ -128,6 +166,12 @@
}
}
+ private boolean shouldRewriteBootstrapMethodSignature() {
+ // TODO(b/210485236): Check for references to the bootstrap method outside of dynamic constant.
+ return !appView.enableWholeProgramOptimizations()
+ && appView.options().getMinApiLevel().isLessThan(AndroidApiLevel.O);
+ }
+
public Collection<CfInstruction> desugarConstDynamicInstruction(
CfConstDynamic invoke,
FreshLocalProvider freshLocalProvider,
@@ -214,13 +258,15 @@
private void invokeBootstrapMethod(ImmutableList.Builder<CfInstruction> instructions) {
assert reference.getBootstrapMethod().type.isInvokeStatic();
- DexMethodHandle bootstrapMethodHandle = reference.getBootstrapMethod();
- DexMethod bootstrapMethodReference = bootstrapMethodHandle.asMethod();
// TODO(b/178172809): Use MethodHandle.invokeWithArguments if supported.
instructions.add(new CfConstNull());
instructions.add(new CfConstString(reference.getName()));
instructions.add(new CfConstClass(reference.getType()));
- instructions.add(new CfInvoke(INVOKESTATIC, bootstrapMethodReference, false));
+ instructions.add(
+ new CfInvoke(
+ INVOKESTATIC,
+ finalBootstrapMethodReference,
+ isFinalBootstrapMethodReferenceOnInterface));
instructions.add(new CfCheckCast(reference.getType()));
}
@@ -317,4 +363,76 @@
assert clazz != null;
this.clazz = clazz;
}
+
+ public void rewriteBootstrapMethodSignatureIfNeeded() {
+ if (!shouldRewriteBootstrapMethodSignature() || behaviour != CACHE_CONSTANT) {
+ return;
+ }
+ DexProgramClass bootstrapMethodHolder =
+ appView.definitionFor(bootstrapMethodReference.getHolderType()).asProgramClass();
+ DexEncodedMethod replacement =
+ bootstrapMethodHolder
+ .getMethodCollection()
+ .replaceDirectMethod(
+ bootstrapMethodReference,
+ encodedMethod -> {
+ MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
+ // Ensure that the bootstrap method is accessible from the generated class.
+ newAccessFlags.unsetPrivate();
+ newAccessFlags.setPublic();
+ DexEncodedMethod newMethod =
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(finalBootstrapMethodReference)
+ .setAccessFlags(newAccessFlags)
+ .setGenericSignature(encodedMethod.getGenericSignature())
+ .setAnnotations(encodedMethod.annotations())
+ .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
+ .setCode(adaptCode(encodedMethod))
+ .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
+ .setApiLevelForCode(encodedMethod.getApiLevelForCode())
+ .build();
+ newMethod.copyMetadata(appView, encodedMethod);
+ return newMethod;
+ });
+ if (replacement != null) {
+ // Since we've copied the code object from an existing method, the code should already be
+ // processed, and thus we don't need to schedule it for processing in D8.
+ assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode();
+ assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode();
+ }
+ // The method might already have been moved by another dynamic constant targeting it.
+ // If so, it must be defined on the holder.
+ ProgramMethod modified =
+ bootstrapMethodHolder.lookupProgramMethod(finalBootstrapMethodReference);
+ assert modified != null;
+ assert modified.getDefinition().isPublicMethod();
+ }
+
+ private DexType mapLookupTypeToObject(DexType type) {
+ return type == appView.dexItemFactory().lookupType ? appView.dexItemFactory().objectType : type;
+ }
+
+ private Code adaptCode(DexEncodedMethod method) {
+ assert behaviour == CACHE_CONSTANT;
+ if (method.getCode().isDexCode()) {
+ return method.getCode();
+ }
+ CfCode code = method.getCode().asCfCode();
+ List<CfInstruction> newInstructions =
+ ListUtils.mapOrElse(
+ code.getInstructions(),
+ instruction ->
+ instruction.isFrame()
+ ? instruction.asFrame().map(this::mapLookupTypeToObject)
+ : instruction);
+ return code.getInstructions() != newInstructions
+ ? new CfCode(
+ method.getHolderType(),
+ code.getMaxStack(),
+ code.getMaxLocals(),
+ newInstructions,
+ code.getTryCatchRanges(),
+ code.getLocalVariables())
+ : code;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 616cc07..77d99e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -155,7 +155,7 @@
}
// Gets the companion class for the interface `type`.
- static DexType getCompanionClassType(DexType type, DexItemFactory factory) {
+ public static DexType getCompanionClassType(DexType type, DexItemFactory factory) {
assert type.isClassType();
String descriptor = type.descriptor.toString();
String ccTypeDescriptor = getCompanionClassDescriptor(descriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index c89f90c..82f80e8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -168,7 +168,10 @@
companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
}
Code code =
- definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference());
+ definition
+ .getCode()
+ .getCodeAsInlining(
+ companion.getReference(), method.getReference(), appView.dexItemFactory());
if (!definition.isStatic()) {
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 3875ddf..d35c7b7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -247,7 +247,7 @@
}
}
if (outValue.getType().isNullType()) {
- addNullDependencies(code, outValue.uniqueUsers(), eligibleEnums);
+ addNullDependencies(code, outValue, eligibleEnums);
}
} else {
if (instruction.isInvokeMethod()) {
@@ -290,7 +290,7 @@
}
}
if (phi.getType().isNullType()) {
- addNullDependencies(code, phi.uniqueUsers(), eligibleEnums);
+ addNullDependencies(code, phi, eligibleEnums);
}
}
}
@@ -520,8 +520,8 @@
|| method == factory.classMethods.getSimpleName;
}
- private void addNullDependencies(IRCode code, Set<Instruction> uses, Set<DexType> eligibleEnums) {
- for (Instruction use : uses) {
+ private void addNullDependencies(IRCode code, Value nullValue, Set<DexType> eligibleEnums) {
+ for (Instruction use : nullValue.uniqueUsers()) {
if (use.isInvokeMethod()) {
InvokeMethod invokeMethod = use.asInvokeMethod();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
@@ -530,7 +530,8 @@
eligibleEnums.add(paramType);
}
}
- if (invokeMethod.isInvokeMethodWithReceiver()) {
+ if (invokeMethod.isInvokeMethodWithReceiver()
+ && invokeMethod.asInvokeMethodWithReceiver().getReceiver() == nullValue) {
DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(invokedMethod.holder);
if (enumClass != null) {
markEnumAsUnboxable(Reason.ENUM_METHOD_CALLED_WITH_NULL_RECEIVER, enumClass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 33f4ba5..dca8e31 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -356,6 +356,10 @@
if (!candidateBuilders.contains(builder)) {
continue;
}
+ if (builder.hasPhiUsers()) {
+ candidateBuilders.remove(builder);
+ continue;
+ }
Map<Instruction, BuilderState> perInstrState = createBuilderState(builder);
perInstrState.put(instr, BuilderState.createRoot());
continue;
@@ -396,6 +400,10 @@
if (!candidateBuilders.contains(builder)) {
continue;
}
+ if (invoke.hasUsedOutValue()) {
+ candidateBuilders.remove(builder);
+ continue;
+ }
Value arg = invoke.inValues().get(1).getAliasedValue();
DexType argType = invoke.getInvokedMethod().proto.parameters.values[0];
String addition = extractConstantArgument(arg, argType);
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index bec8073..f948457 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -180,6 +180,7 @@
ReservedFieldNamingState reservedNames =
getOrCreateReservedFieldNamingState(clazz.type);
+ // TODO(b/213041051): This could avoid duplication of strings downwards.
FieldNamingState state = parentState.createChildState(reservedNames);
if (clazz.isProgramClass()) {
clazz.asProgramClass().forEachProgramField(field -> renameField(field, state));
@@ -191,8 +192,8 @@
}
private void renameFieldsInInterfaces(Collection<DexClass> interfaces) {
- InterfacePartitioning partioning = new InterfacePartitioning(this);
- for (Set<DexClass> partition : partioning.sortedPartitions(interfaces)) {
+ InterfacePartitioning partitioning = new InterfacePartitioning(this);
+ for (Set<DexClass> partition : partitioning.sortedPartitions(interfaces)) {
renameFieldsInInterfacePartition(partition);
}
}
@@ -231,11 +232,10 @@
if (!visited.add(implementationType)) {
continue;
}
-
DexClass implementation = appView.definitionFor(implementationType);
if (implementation != null) {
getOrCreateReservedFieldNamingState(implementationType)
- .includeReservations(namesToBeReservedInImplementsSubclasses);
+ .setInterfaceMinificationState(namesToBeReservedInImplementsSubclasses);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 3ba2467..8067817 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -654,9 +654,8 @@
DexType frontierType = minifierState.getFrontier(clazz.type);
iState.addReservationType(frontierType);
// The reservation state should already be added, but if a class is
- // extending
- // an interface, we will not visit the class during the sub-type
- // traversel
+ // extending an interface, we will not visit the class during the
+ // sub-type traversel.
if (minifierState.getReservationState(clazz.type) == null) {
minifierState.allocateReservationStateAndReserve(
clazz.type, frontierType);
diff --git a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
index 4e3e98c..258646e 100644
--- a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
@@ -14,16 +14,32 @@
class ReservedFieldNamingState extends FieldNamingStateBase<InternalState> {
+ private ReservedFieldNamingState interfaceMinificationState = null;
+
ReservedFieldNamingState(AppView<? extends AppInfoWithClassHierarchy> appView) {
super(appView, new IdentityHashMap<>());
}
boolean isReserved(DexString name, DexType type) {
- return getReservedByName(name, type) != null;
+ return getReservedByName(name, type) != null
+ || getReservedByNameInInterfaces(name, type) != null;
}
- DexString getReservedByName(DexString name, DexType type) {
- InternalState internalState = getInternalState(type);
+ private DexString getReservedByName(DexString name, DexType type) {
+ DexString reservedByNameInState = getReservedByNameInState(getInternalState(type), name);
+ if (reservedByNameInState != null) {
+ return reservedByNameInState;
+ }
+ return getReservedByNameInInterfaces(name, type);
+ }
+
+ private DexString getReservedByNameInInterfaces(DexString name, DexType type) {
+ return interfaceMinificationState == null
+ ? null
+ : getReservedByNameInState(interfaceMinificationState.getInternalState(type), name);
+ }
+
+ private static DexString getReservedByNameInState(InternalState internalState, DexString name) {
return internalState == null ? null : internalState.getReservedByName(name);
}
@@ -35,12 +51,28 @@
for (Map.Entry<DexType, InternalState> entry : reservedNames.internalStates.entrySet()) {
getOrCreateInternalState(entry.getKey()).includeReservations(entry.getValue());
}
+ includeInterfaceReservationState(reservedNames);
}
void includeReservationsFromBelow(ReservedFieldNamingState reservedNames) {
for (Map.Entry<DexType, InternalState> entry : reservedNames.internalStates.entrySet()) {
getOrCreateInternalState(entry.getKey()).includeReservationsFromBelow(entry.getValue());
}
+ includeInterfaceReservationState(reservedNames);
+ }
+
+ private void includeInterfaceReservationState(ReservedFieldNamingState reservedNames) {
+ if (reservedNames.interfaceMinificationState != null) {
+ assert interfaceMinificationState == null
+ || interfaceMinificationState == reservedNames.interfaceMinificationState;
+ interfaceMinificationState = reservedNames.interfaceMinificationState;
+ }
+ }
+
+ void setInterfaceMinificationState(ReservedFieldNamingState namingState) {
+ assert namingState != null;
+ assert interfaceMinificationState == null;
+ this.interfaceMinificationState = namingState;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
index 24c6a60..06fdc4c 100644
--- a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -131,6 +131,10 @@
return results != null ? results.toArray(emptyArray) : original;
}
+ public static <T> T[] filter(T[] original, Predicate<T> test, T[] emptyArray) {
+ return map(original, e -> test.test(e) ? e : null, emptyArray);
+ }
+
public static int[] createIdentityArray(int size) {
int[] array = new int[size];
for (int i = 0; i < size; i++) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0bfc079..1000a75 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1869,8 +1869,12 @@
return sourceFileProvider != null && sourceFileProvider.allowDiscardingSourceFile();
}
- public boolean canUseDexPcAsDebugInformation() {
- return lineNumberOptimization == LineNumberOptimization.ON
+ public boolean canUseDexPc2PcAsDebugInformation() {
+ return lineNumberOptimization == LineNumberOptimization.ON;
+ }
+
+ public boolean canUseNativeDexPcInsteadOfDebugInfo() {
+ return canUseDexPc2PcAsDebugInformation()
&& hasMinApi(AndroidApiLevel.O)
&& allowDiscardingResidualDebugInfo();
}
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 71da5cc..50eeb68 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexDebugEventBuilder;
import com.android.tools.r8.graph.DexDebugEventVisitor;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -73,7 +74,10 @@
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
@@ -345,6 +349,117 @@
return mapId;
}
+ private interface PcBasedDebugInfoRecorder {
+ /** Callback to record a code object with a given max instruction PC and parameter count. */
+ void recordPcMappingFor(DexCode code, int lastInstructionPc, int parameterCount);
+
+ /** Callback to record a code object with only a single "line". */
+ void recordSingleLineFor(DexCode code, int parameterCount);
+
+ void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc);
+
+ /**
+ * Install the correct debug info objects.
+ *
+ * <p>Must be called after all recordings have been given to allow computing the debug info
+ * items to be installed.
+ */
+ void updateDebugInfoInCodeObjects();
+ }
+
+ private static class Pc2PcMappingSupport implements PcBasedDebugInfoRecorder {
+
+ // Some DEX VMs require matching parameter count in methods and debug info.
+ // Record the max pc for each parameter count so we can share the param count objects.
+ private Int2IntMap paramToMaxPc = new Int2IntOpenHashMap();
+
+ private final List<Pair<Integer, DexCode>> codesToUpdate = new ArrayList<>();
+
+ // We can only drop single-line debug info if it is OK to lose the source-file info.
+ // This list is null if we must retain single-line entries.
+ private final List<DexCode> singleLineCodesToClear;
+
+ public Pc2PcMappingSupport(boolean allowDiscardingSourceFile) {
+ singleLineCodesToClear = allowDiscardingSourceFile ? new ArrayList<>() : null;
+ }
+
+ @Override
+ public void recordPcMappingFor(DexCode code, int lastInstructionPc, int parameterCount) {
+ codesToUpdate.add(new Pair<>(parameterCount, code));
+ int existing = paramToMaxPc.getOrDefault(parameterCount, -1);
+ if (existing < lastInstructionPc) {
+ paramToMaxPc.put(parameterCount, lastInstructionPc);
+ }
+ }
+
+ @Override
+ public void recordSingleLineFor(DexCode code, int parameterCount) {
+ if (singleLineCodesToClear != null) {
+ singleLineCodesToClear.add(code);
+ return;
+ }
+ int lastInstructionPc = ArrayUtils.last(code.instructions).getOffset();
+ recordPcMappingFor(code, lastInstructionPc, parameterCount);
+ }
+
+ @Override
+ public void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc) {
+ if (singleLineCodesToClear != null) {
+ singleLineCodesToClear.add(code);
+ return;
+ }
+ recordPcMappingFor(code, lastInstructionPc, parameterCount);
+ }
+
+ @Override
+ public void updateDebugInfoInCodeObjects() {
+ Int2ReferenceMap<DexDebugInfo> debugInfos =
+ new Int2ReferenceOpenHashMap<>(paramToMaxPc.size());
+ codesToUpdate.forEach(
+ entry -> {
+ int parameterCount = entry.getFirst();
+ DexCode code = entry.getSecond();
+ DexDebugInfo debugInfo =
+ debugInfos.computeIfAbsent(
+ parameterCount,
+ key -> buildPc2PcDebugInfo(paramToMaxPc.get(key), parameterCount));
+ code.setDebugInfo(debugInfo);
+ });
+ if (singleLineCodesToClear != null) {
+ singleLineCodesToClear.forEach(c -> c.setDebugInfo(null));
+ }
+ }
+
+ private static DexDebugInfo buildPc2PcDebugInfo(int lastInstructionPc, int parameterCount) {
+ return new DexDebugInfo.PcBasedDebugInfo(parameterCount, lastInstructionPc);
+ }
+ }
+
+ private static class NativePcSupport implements PcBasedDebugInfoRecorder {
+
+ @Override
+ public void recordPcMappingFor(DexCode code, int lastInstructionPc, int length) {
+ // Strip the info in full as the runtime will emit the PC directly.
+ code.setDebugInfo(null);
+ }
+
+ @Override
+ public void recordSingleLineFor(DexCode code, int parameterCount) {
+ // Strip the info at once as it does not conflict with any PC mapping update.
+ code.setDebugInfo(null);
+ }
+
+ @Override
+ public void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc) {
+ recordSingleLineFor(code, parameterCount);
+ }
+
+ @Override
+ public void updateDebugInfoInCodeObjects() {
+ // Already null out the info so nothing to do.
+ }
+ }
+
public static ClassNameMapper run(
AppView<?> appView,
DexApplication application,
@@ -358,6 +473,11 @@
Map<DexMethod, OutlineFixupBuilder> outlinesToFix = new IdentityHashMap<>();
+ PcBasedDebugInfoRecorder pcBasedDebugInfo =
+ appView.options().canUseNativeDexPcInsteadOfDebugInfo()
+ ? new NativePcSupport()
+ : new Pc2PcMappingSupport(appView.options().allowDiscardingResidualDebugInfo());
+
// Collect which files contain which classes that need to have their line numbers optimized.
for (DexProgramClass clazz : application.classes()) {
boolean isSyntheticClass = appView.getSyntheticItems().isSyntheticClass(clazz);
@@ -440,11 +560,13 @@
List<MappedPosition> mappedPositions;
Code code = method.getCode();
boolean canUseDexPc =
- appView.options().canUseDexPcAsDebugInformation() && methods.size() == 1;
+ appView.options().canUseDexPc2PcAsDebugInformation() && methods.size() == 1;
if (code != null) {
if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
if (canUseDexPc) {
- mappedPositions = optimizeDexCodePositionsForPc(method, appView, kotlinRemapper);
+ mappedPositions =
+ optimizeDexCodePositionsForPc(
+ method, appView, kotlinRemapper, pcBasedDebugInfo);
} else {
mappedPositions =
optimizeDexCodePositions(
@@ -615,8 +737,8 @@
if (method.getCode().isDexCode()
&& method.getCode().asDexCode().getDebugInfo()
== DexDebugInfoForSingleLineMethod.getInstance()) {
- assert appView.options().allowDiscardingResidualDebugInfo();
- method.getCode().asDexCode().setDebugInfo(null);
+ pcBasedDebugInfo.recordSingleLineFor(
+ method.getCode().asDexCode(), method.getParameters().size());
}
} // for each method of the group
} // for each method group, grouped by name
@@ -625,6 +747,9 @@
// Fixup all outline positions
outlinesToFix.values().forEach(OutlineFixupBuilder::fixup);
+ // Update all the debug-info objects.
+ pcBasedDebugInfo.updateDebugInfoInCodeObjects();
+
return classNameMapperBuilder.build();
}
@@ -727,7 +852,7 @@
}
if (code.isDexCode()) {
DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
- return dexDebugInfo == null ? 0 : dexDebugInfo.startLine;
+ return dexDebugInfo == null ? 0 : dexDebugInfo.getStartLine();
} else if (code.isCfCode()) {
List<CfInstruction> instructions = code.asCfCode().getInstructions();
for (CfInstruction instruction : instructions) {
@@ -823,7 +948,10 @@
if (debugInfo == null) {
return false;
}
- for (DexDebugEvent event : debugInfo.events) {
+ if (debugInfo.isPcBasedInfo()) {
+ return true;
+ }
+ for (DexDebugEvent event : debugInfo.asEventBasedInfo().events) {
if (event instanceof DexDebugEvent.Default) {
return true;
}
@@ -851,7 +979,10 @@
// Do the actual processing for each method.
DexApplication application = appView.appInfo().app();
DexCode dexCode = method.getCode().asDexCode();
- DexDebugInfo debugInfo = dexCode.getDebugInfo();
+ // TODO(b/213411850): Do we need to reconsider conversion here to support pc-based D8 merging?
+ EventBasedDebugInfo debugInfo =
+ DexDebugInfo.convertToEventBased(dexCode, appView.dexItemFactory());
+ assert debugInfo != null;
List<DexDebugEvent> processedEvents = new ArrayList<>();
PositionEventEmitter positionEventEmitter =
@@ -949,8 +1080,8 @@
return mappedPositions;
}
- DexDebugInfo optimizedDebugInfo =
- new DexDebugInfo(
+ EventBasedDebugInfo optimizedDebugInfo =
+ new EventBasedDebugInfo(
positionEventEmitter.getStartLine(),
debugInfo.parameters,
processedEvents.toArray(DexDebugEvent.EMPTY_ARRAY));
@@ -985,14 +1116,19 @@
}
private static List<MappedPosition> optimizeDexCodePositionsForPc(
- DexEncodedMethod method, AppView<?> appView, PositionRemapper positionRemapper) {
+ DexEncodedMethod method,
+ AppView<?> appView,
+ PositionRemapper positionRemapper,
+ PcBasedDebugInfoRecorder debugInfoProvider) {
List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
DexCode dexCode = method.getCode().asDexCode();
- DexDebugInfo debugInfo = dexCode.getDebugInfo();
-
+ // TODO(b/213411850): Do we need to reconsider conversion here to support pc-based D8 merging?
+ EventBasedDebugInfo debugInfo =
+ DexDebugInfo.convertToEventBased(dexCode, appView.dexItemFactory());
+ assert debugInfo != null;
+ BooleanBox singleOriginalLine = new BooleanBox(true);
Pair<Integer, Position> lastPosition = new Pair<>();
-
DexDebugEventVisitor visitor =
new DexDebugPositionState(
debugInfo.startLine,
@@ -1001,7 +1137,12 @@
public void visit(Default defaultEvent) {
super.visit(defaultEvent);
assert getCurrentLine() >= 0;
+ Position currentPosition = getPositionFromPositionState(this);
if (lastPosition.getSecond() != null) {
+ if (singleOriginalLine.isTrue()
+ && !currentPosition.equals(lastPosition.getSecond())) {
+ singleOriginalLine.set(false);
+ }
remapAndAddForPc(
lastPosition.getFirst(),
getCurrentPc(),
@@ -1010,7 +1151,7 @@
mappedPositions);
}
lastPosition.setFirst(getCurrentPc());
- lastPosition.setSecond(getPositionFromPositionState(this));
+ lastPosition.setSecond(currentPosition);
resetOutlineInformation();
}
};
@@ -1019,22 +1160,32 @@
event.accept(visitor);
}
+ int lastInstructionPc = ArrayUtils.last(dexCode.instructions).getOffset();
if (lastPosition.getSecond() != null) {
- int lastPc = dexCode.instructions[dexCode.instructions.length - 1].getOffset();
remapAndAddForPc(
lastPosition.getFirst(),
- lastPc + 1,
+ lastInstructionPc + 1,
lastPosition.getSecond(),
positionRemapper,
mappedPositions);
}
- dexCode.setDebugInfo(null);
+ // If we only have one original line we can always retrace back uniquely.
+ assert !mappedPositions.isEmpty();
+ if (singleOriginalLine.isTrue()
+ && lastPosition.getSecond() != null
+ && !mappedPositions.get(0).isOutlineCaller()) {
+ dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
+ debugInfoProvider.recordSingleLineFor(
+ dexCode, method.getParameters().size(), lastInstructionPc);
+ } else {
+ debugInfoProvider.recordPcMappingFor(dexCode, lastInstructionPc, debugInfo.parameters.length);
+ }
return mappedPositions;
}
private static boolean verifyIdentityMapping(
- DexDebugInfo originalDebugInfo, DexDebugInfo optimizedDebugInfo) {
+ EventBasedDebugInfo originalDebugInfo, EventBasedDebugInfo optimizedDebugInfo) {
assert optimizedDebugInfo.startLine == originalDebugInfo.startLine;
assert optimizedDebugInfo.events.length == originalDebugInfo.events.length;
for (int i = 0; i < originalDebugInfo.events.length; ++i) {
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index 04d801c..be77df56 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -59,8 +59,13 @@
}
@Override
- public void scope(String title, TimingScope fn) {
- // Ignore.
+ public <T> T scope(String title, TimingScope<T> fn) {
+ return fn.apply();
+ }
+
+ @Override
+ public void vscope(String title, VoidTimingScope fn) {
+ fn.apply();
}
};
@@ -402,7 +407,7 @@
top.report(0, top);
}
- public void scope(String title, TimingScope fn) {
+ public void vscope(String title, VoidTimingScope fn) {
begin(title);
try {
fn.apply();
@@ -411,7 +416,20 @@
}
}
- public interface TimingScope {
+ public <T> T scope(String title, TimingScope<T> fn) {
+ begin(title);
+ try {
+ return fn.apply();
+ } finally {
+ end();
+ }
+ }
+
+ public interface TimingScope<T> {
+ T apply();
+ }
+
+ public interface VoidTimingScope {
void apply();
}
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index aec53db..ae8a263 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -61,6 +61,23 @@
return self;
}
+ public T applyIf(
+ boolean value,
+ ThrowableConsumer<T> trueConsumer,
+ boolean value2,
+ ThrowableConsumer<T> trueConsumer2,
+ ThrowableConsumer<T> falseConsumer) {
+ T self = self();
+ if (value) {
+ trueConsumer.acceptWithRuntimeException(self);
+ } else if (value2) {
+ trueConsumer2.acceptWithRuntimeException(self);
+ } else {
+ falseConsumer.acceptWithRuntimeException(self);
+ }
+ return self;
+ }
+
@Deprecated
public RR run(String mainClass)
throws CompilationFailedException, ExecutionException, IOException {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index c41c5e2..4b94e1c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -48,6 +48,7 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::disableCheckAllApiReferencesAreNotUnknown)
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
index 302cca5..f946a84 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
@@ -47,6 +47,7 @@
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
index 911731b..fc29914 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
@@ -45,6 +45,7 @@
.addKeepMainRule(Main.class)
.apply(setMockApiLevelForMethod(apiLevel22, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.compile()
.inspect(inspector -> verifyThat(inspector, parameters, callApi).inlinedInto(callCallApi))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
index 9d667c1..73132ae 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
@@ -48,6 +48,7 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForClass(ApiType.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.addRunClasspathClasses(ApiType.class)
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
index c4308ed..53b4942 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
@@ -47,6 +47,7 @@
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index 4979620..9a239f8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -55,6 +55,7 @@
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
index db7dc06..71a742e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
@@ -52,6 +52,7 @@
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(methodReference, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 3ccf46d..605865f 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -11,11 +11,10 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexDebugEntry;
import com.android.tools.r8.graph.DexDebugEntryBuilder;
-import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -36,15 +35,18 @@
private final DexEncodedMethod method;
private final List<DexDebugEntry> entries;
- final DexDebugInfo info;
+ final EventBasedDebugInfo info;
public DebugInfoInspector(DexEncodedMethod method, DexItemFactory factory) {
this.method = method;
- info = method.getCode().asDexCode().getDebugInfo();
- if (info != null) {
+ EventBasedDebugInfo debugInfo =
+ DexDebugInfo.convertToEventBased(method.getCode().asDexCode(), factory);
+ if (debugInfo != null) {
+ info = debugInfo;
entries = new DexDebugEntryBuilder(method, factory).build();
checkConsistentEntries();
} else {
+ info = null;
entries = Collections.emptyList();
}
}
@@ -58,24 +60,6 @@
this(new CodeInspector(app), clazz, method);
}
- public boolean hasLocalsInfo() {
- DexDebugInfo dexInfo = method.getCode().asDexCode().getDebugInfo();
- if (info == null || dexInfo == null) {
- return false;
- }
- for (DexString parameter : dexInfo.parameters) {
- if (parameter != null) {
- return true;
- }
- }
- for (DexDebugEvent event : dexInfo.events) {
- if (event instanceof DexDebugEvent.StartLocal) {
- return true;
- }
- }
- return false;
- }
-
public void checkStartLine(int i) {
assertEquals(i, info.startLine);
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java
index e64b7d7..008d3cf 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java
@@ -71,6 +71,7 @@
.getCode()
.asDexCode()
.getDebugInfo()
+ .asEventBasedInfo()
.events)
.anyMatch(e -> e instanceof SetFile));
})
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
index a2f71b4..c31af39 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceFrameResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -68,7 +67,6 @@
.addKeepMainRule(MAIN)
.addKeepMethodRules(MAIN, "void overloaded(...)")
.addKeepAttributeLineNumberTable()
- .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.enableAlwaysInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
index de9685e..4810b1d 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
@@ -34,7 +34,7 @@
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
- private static final String EXPECTED_OUTPUT =
+ private static final String EXPECTED_OUTPUT_WITH_METHOD_HANDLES =
StringUtils.lines(
"Hello, world!",
"myConstant",
@@ -42,6 +42,14 @@
"java.lang.invoke.MethodHandles$Lookup",
"java.lang.String",
"java.lang.Class");
+ private static final String EXPECTED_OUTPUT_WITHOUT_METHOD_HANDLES =
+ StringUtils.lines(
+ "Hello, world!",
+ "myConstant",
+ "3",
+ "java.lang.Object",
+ "java.lang.String",
+ "java.lang.Class");
private static final String EXPECTED_OUTPUT_R8 =
StringUtils.lines("Hello, world!", "No myConstant method");
@@ -56,7 +64,7 @@
testForJvm()
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), MAIN_CLASS)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ .assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES);
}
@Test
@@ -64,24 +72,22 @@
testForDesugaring(parameters)
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), MAIN_CLASS)
- // TODO(b/210485236): This should never fail.
.applyIf(
// When not desugaring the CF code requires JDK 11.
DesugarTestConfiguration::isNotDesugared,
r -> {
if (parameters.isCfRuntime()
&& parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
- r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES);
} else {
r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
}
},
c ->
DesugarTestConfiguration.isDesugared(c)
- && parameters.isDexRuntime()
- && parameters.asDexRuntime().getVersion().isOlderThan(Version.V8_1_0),
- r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_WITHOUT_METHOD_HANDLES),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES));
}
@Test
@@ -110,11 +116,10 @@
parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
b -> b.addDontWarn(MethodHandles.Lookup.class))
.run(parameters.getRuntime(), MAIN_CLASS)
- // TODO(b/210485236): This should never fail.
.applyIf(
parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
- b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES));
}
private byte[] getTransformedClasses() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..a09ae6a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
@@ -0,0 +1,151 @@
+// Copyright (c) 2022, 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstantDynamicInDefaultInterfaceMethodTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true");
+ private static final Class<?> MAIN_CLASS = A.class;
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForJvm()
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ })
+ .applyIf(
+ DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ // TODO(b/198142613): There should not be a warnings on class references which are
+ // desugared away.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+ // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+ .applyIf(
+ parameters.isCfRuntime(),
+ b -> {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ b.compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unsupported dynamic constant (not desugaring)")));
+ }));
+ },
+ // TODO(b/210485236): This should not fail for R8.
+ !parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring(),
+ b ->
+ b.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertFailureWithErrorThatThrows(NoSuchMethodError.class),
+ b ->
+ b.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ private byte[] getTransformedClasses() throws Exception {
+ return transformer(I.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", I.class, "myConstant", "constantName", Object.class)
+ .transformConstStringToConstantDynamic(
+ "condy2", I.class, "myConstant", "constantName", Object.class)
+ .setPrivate(
+ I.class.getDeclaredMethod(
+ "myConstant", MethodHandles.Lookup.class, String.class, Class.class))
+ .transform();
+ }
+
+ public interface I {
+
+ default Object f() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ default Object g() {
+ return "condy2"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ /* private */ static Object myConstant(
+ MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return new Object();
+ }
+ }
+
+ public static class A implements I {
+ public static void main(String[] args) {
+ A a = new A();
+ System.out.println(a.f() != null);
+ System.out.println(a.f() == a.g());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java
index 79fc834..8524f5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java
@@ -49,13 +49,20 @@
public static JacocoClasses testClasses;
private static final String MAIN_CLASS = TestRunner.class.getTypeName();
- private static final String EXPECTED_OUTPUT =
+ private static final String EXPECTED_OUTPUT_WITH_METHOD_HANDLES =
StringUtils.lines(
jacocoBootstrapMethodName,
"3",
"java.lang.invoke.MethodHandles$Lookup",
"java.lang.String",
"java.lang.Class");
+ private static final String EXPECTED_OUTPUT_WITHOUT_METHOD_HANDLES =
+ StringUtils.lines(
+ jacocoBootstrapMethodName,
+ "3",
+ "java.lang.Object",
+ "java.lang.String",
+ "java.lang.Class");
@BeforeClass
public static void setUpInput() throws IOException {
@@ -85,7 +92,7 @@
.addProgramFiles(testClasses.getOriginal())
.enableJaCoCoAgent(ToolHelper.JACOCO_AGENT, agentOutputOnTheFly)
.run(parameters.getRuntime(), MAIN_CLASS)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ .assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES);
checkJacocoReport(agentOutputOnTheFly);
// Run the instrumented code.
@@ -94,7 +101,7 @@
.addProgramFiles(testClasses.getInstrumented())
.configureJaCoCoAgentForOfflineInstrumentedCode(ToolHelper.JACOCO_AGENT, agentOutputOffline)
.run(parameters.getRuntime(), MAIN_CLASS)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ .assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES);
checkJacocoReport(agentOutputOffline);
}
@@ -108,11 +115,10 @@
.setMinApi(parameters.getApiLevel())
.compile()
.runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
- // TODO(b/210485236): This should never fail.
.applyIf(
- parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
- b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
- b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT_WITHOUT_METHOD_HANDLES),
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES));
checkJacocoReport(agentOutput);
}
@@ -160,11 +166,10 @@
"javax.management.**")
.compile()
.runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
- // TODO(b/210485236): This should never fail.
.applyIf(
parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
- b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT_WITH_METHOD_HANDLES));
checkJacocoReport(agentOutput);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
index 9c33f50..46261e5 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.jacoco.JacocoClasses;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -50,7 +51,7 @@
public JacocoClasses testClasses;
private static final String MAIN_CLASS = TestRunner.class.getTypeName();
- private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!", "Hello from I!");
@BeforeClass
public static void setUpInput() throws IOException {
@@ -85,7 +86,7 @@
.run(parameters.getRuntime(), MAIN_CLASS)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
List<String> onTheFlyReport = testClasses.generateReport(agentOutputOnTheFly);
- assertEquals(2, onTheFlyReport.size());
+ assertEquals(3, onTheFlyReport.size());
// Run the instrumented code.
Path agentOutputOffline = output.resolve("offline");
@@ -114,7 +115,7 @@
// TODO(sgjesse): Need to figure out why there is no instrumentation output for newer VMs.
if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
List<String> report = testClasses.generateReport(agentOutput);
- assertEquals(2, report.size());
+ assertEquals(3, report.size());
} else {
assertFalse(Files.exists(agentOutput));
}
@@ -130,16 +131,23 @@
private static JacocoClasses testClasses(TemporaryFolder temp, CfVersion version)
throws IOException {
return new JacocoClasses(
- transformer(TestRunner.class)
- .setVersion(version) /*.setClassDescriptor("LTestRunner;")*/
- .transform(),
+ ImmutableList.of(
+ transformer(TestRunner.class).setVersion(version).transform(),
+ transformer(I.class).setVersion(version).transform()),
temp);
}
- static class TestRunner {
+ interface I {
+ default void m() {
+ System.out.println("Hello from I!");
+ }
+ }
+
+ static class TestRunner implements I {
public static void main(String[] args) {
System.out.println("Hello, world!");
+ new TestRunner().m();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DateTimeFormatterTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DateTimeFormatterTest.java
new file mode 100644
index 0000000..2d4640a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DateTimeFormatterTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.FormatStyle;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DateTimeFormatterTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String expectedOutputDesugaredLib =
+ StringUtils.lines("2/3/01 4:05 AM - Feb 3, 1 4:05 AM");
+ private static final String expectedOutput =
+ StringUtils.lines("2/3/01, 4:05 AM - Feb 3, 1, 4:05 AM");
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public DateTimeFormatterTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ D8TestRunResult run =
+ testForD8()
+ .addLibraryFiles(getLibraryFile())
+ .addInnerClasses(DateTimeFormatterTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setIncludeClassesChecksum(true)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class);
+ if (requiresTimeDesugaring(parameters)) {
+ run.assertSuccessWithOutput(expectedOutputDesugaredLib);
+ } else {
+ run.assertSuccessWithOutput(expectedOutput);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ R8TestRunResult run =
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .noMinification()
+ .addKeepMainRule(TestClass.class)
+ .addInnerClasses(DateTimeFormatterTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class);
+ if (requiresTimeDesugaring(parameters)) {
+ run.assertSuccessWithOutput(expectedOutputDesugaredLib);
+ } else {
+ run.assertSuccessWithOutput(expectedOutput);
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ // See b/210885987: ANRS reported on Android 12 on the methods:
+ // - j$.time.format.DateTimeFormatterBuilder.append (DateTimeFormatterBuilder.java)
+ // - j$.time.format.DateTimeFormatter.<clinit> (DateTimeFormatter.java)
+ // - j$.time.format.DateTimeFormatter.ofLocalizedDateTime (DateTimeFormatter.java)
+ DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+ DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
+ DateTimeFormatter formatter2 =
+ DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT);
+ DateTimeFormatter formatter =
+ builder.append(formatter1).appendLiteral(" - ").append(formatter2).toFormatter();
+ LocalDateTime dateTime = LocalDateTime.of(1, 2, 3, 4, 5);
+ String str = dateTime.format(formatter);
+ System.out.println(str);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 5aeaddc..9c4c113 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.GraphLens;
@@ -68,10 +69,13 @@
@Test
public void testEmptyDebugInfo() {
- DexDebugInfo debugInfo = new DexDebugInfo(1, DexString.EMPTY_ARRAY, new DexDebugEvent[]{});
+ DexDebugInfo debugInfo =
+ new EventBasedDebugInfo(1, DexString.EMPTY_ARRAY, new DexDebugEvent[] {});
DebugBytecodeWriter writer =
new DebugBytecodeWriter(
- debugInfo, emptyObjectTObjectMapping(), GraphLens.getIdentityLens());
+ DexDebugInfo.convertToWritable(debugInfo),
+ emptyObjectTObjectMapping(),
+ GraphLens.getIdentityLens());
Assert.assertEquals(3, writer.generate().length);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java
index 8552e48..cc582ea 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java
@@ -158,29 +158,33 @@
public void testPhiWithDifferentNewInstance() {
buildAndCheckIR(
"phiWithDifferentNewInstance",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(2, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
+ checkOptimizerStates(
+ appView,
+ optimizer -> {
+ assertEquals(0, optimizer.analysis.builderStates.size());
+ for (Value builder : optimizer.analysis.builderStates.keySet()) {
+ Map<Instruction, BuilderState> perBuilderState =
+ optimizer.analysis.builderStates.get(builder);
+ checkBuilderState(optimizer, perBuilderState, null, false);
+ }
+ assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
+ }));
}
@Test
public void testPhiAtInit() {
buildAndCheckIR(
"phiAtInit",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(2, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
+ checkOptimizerStates(
+ appView,
+ optimizer -> {
+ assertEquals(0, optimizer.analysis.builderStates.size());
+ for (Value builder : optimizer.analysis.builderStates.keySet()) {
+ Map<Instruction, BuilderState> perBuilderState =
+ optimizer.analysis.builderStates.get(builder);
+ checkBuilderState(optimizer, perBuilderState, null, false);
+ }
+ assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
+ }));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
index e18c8ea..2a81ef0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
@@ -265,7 +265,7 @@
@Test
public void testPhiAtInit() {
- int expectedNumOfNewBuilder = 2;
+ int expectedNumOfNewBuilder = 0;
boolean expectToMeetToString = false;
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.M)) {
expectedNumOfNewBuilder = 1;
@@ -290,15 +290,17 @@
public void testPhiWithDifferentInits() {
buildAndCheckIR(
"phiWithDifferentInits",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(2, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
+ checkOptimizerStates(
+ appView,
+ optimizer -> {
+ assertEquals(0, optimizer.analysis.builderStates.size());
+ for (Value builder : optimizer.analysis.builderStates.keySet()) {
+ Map<Instruction, BuilderState> perBuilderState =
+ optimizer.analysis.builderStates.get(builder);
+ checkBuilderState(optimizer, perBuilderState, null, false);
+ }
+ assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
+ }));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithLoopTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithLoopTest.java
new file mode 100644
index 0000000..8cc98c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithLoopTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2022, 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.optimize.string;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StringBuilderWithLoopTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Utils.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1", "2");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StringBuilder sb1 = new StringBuilder();
+ StringBuilder sb2 = new StringBuilder();
+ int i = 1;
+ String line;
+ while ((line = Utils.readLine(i)) != null) {
+ StringBuilder sb = i == 1 ? sb1 : sb2;
+ sb.append(line);
+ i++;
+ }
+ System.out.println(sb1.toString());
+ System.out.println(sb2.toString());
+ }
+ }
+
+ static class Utils {
+
+ // @Keep
+ static String readLine(int i) {
+ if (i <= 2) {
+ return Integer.toString(i);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java b/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java
index b98cc63..dd2700f 100644
--- a/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java
+++ b/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -28,22 +29,29 @@
private final Path originalJar;
private final Path instrumentedJar;
- // Create JacocoClasses with just one class provided as bytes.
+ // Create JacocoClasses with just one class provided as class file bytes.
public JacocoClasses(byte[] clazz, TemporaryFolder temp) throws IOException {
+ this(ImmutableList.of(clazz), temp);
+ }
+
+ // Create JacocoClasses with multiple classes provided as class file bytes.
+ public JacocoClasses(List<byte[]> classes, TemporaryFolder temp) throws IOException {
this.temp = temp;
dir = temp.newFolder().toPath();
// Write the class to a .class file with package sub-directories.
- String typeName = TestBase.extractClassName(clazz);
- int lastDotIndex = typeName.lastIndexOf('.');
- String pkg = typeName.substring(0, lastDotIndex);
- String baseFileName = typeName.substring(lastDotIndex + 1) + CLASS_EXTENSION;
Path original = dir.resolve("original");
- Files.createDirectories(original);
- Path packageDir = original.resolve(pkg.replace(JAVA_PACKAGE_SEPARATOR, File.separatorChar));
- Files.createDirectories(packageDir);
- Path classFile = packageDir.resolve(baseFileName);
- Files.write(classFile, clazz);
+ for (byte[] clazz : classes) {
+ String typeName = TestBase.extractClassName(clazz);
+ int lastDotIndex = typeName.lastIndexOf('.');
+ String pkg = typeName.substring(0, lastDotIndex);
+ String baseFileName = typeName.substring(lastDotIndex + 1) + CLASS_EXTENSION;
+ Files.createDirectories(original);
+ Path packageDir = original.resolve(pkg.replace(JAVA_PACKAGE_SEPARATOR, File.separatorChar));
+ Files.createDirectories(packageDir);
+ Path classFile = packageDir.resolve(baseFileName);
+ Files.write(classFile, clazz);
+ }
// Run offline instrumentation.
Path instrumented = dir.resolve("instrumented");
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index a6505c8..3531c36 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.debuginfo.DebugInfoInspector;
import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
@@ -24,65 +24,70 @@
public void testSwap() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
- MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of("Ljava/lang/String;"), "V",
- // The first three vars are out-of-order to verify that the order is not relied on.
- ".var 5 is t I from L4 to L6",
- ".var 1 is bar Ljava/lang/String; from L0 to L9",
- ".var 0 is this LTest; from L0 to L9",
- ".var 2 is x I from L1 to L9",
- ".var 3 is y I from L2 to L9",
- ".var 4 is z I from L3 to L9",
- ".var 5 is foobar Ljava/lang/String; from L7 to L9",
- ".limit locals 6",
- ".limit stack 2",
- "L0:",
- ".line 23",
- " iconst_1",
- " istore 2",
- "L1:",
- ".line 24",
- " iconst_2",
- " istore 3",
- "L2:",
- ".line 25",
- " iconst_3",
- " istore 4",
- "L3:",
- " .line 27",
- " iload 3",
- " istore 5",
- "L4:",
- " .line 28",
- " iload 2",
- " istore 3",
- "L5:",
- " .line 29",
- " iload 5",
- " istore 2",
- "L6:",
- " .line 32",
- " new java/lang/StringBuilder",
- " dup",
- " invokespecial java/lang/StringBuilder/<init>()V",
- " ldc \"And the value of y is: \"",
- " invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- " iload 2",
- " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
- " iload 3",
- " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
- " iload 4",
- " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
- " invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;",
- " astore 5",
- "L7:",
- " .line 34",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " aload 5",
- " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
- "L8:",
- " .line 35",
- " return",
- "L9:");
+ MethodSignature foo =
+ clazz.addVirtualMethod(
+ "foo",
+ ImmutableList.of("Ljava/lang/String;"),
+ "V",
+ // The first three vars are out-of-order to verify that the order is not relied on.
+ ".var 5 is t I from L4 to L6",
+ ".var 1 is bar Ljava/lang/String; from L0 to L9",
+ ".var 0 is this LTest; from L0 to L9",
+ ".var 2 is x I from L1 to L9",
+ ".var 3 is y I from L2 to L9",
+ ".var 4 is z I from L3 to L9",
+ ".var 5 is foobar Ljava/lang/String; from L7 to L9",
+ ".limit locals 6",
+ ".limit stack 2",
+ "L0:",
+ ".line 23",
+ " iconst_1",
+ " istore 2",
+ "L1:",
+ ".line 24",
+ " iconst_2",
+ " istore 3",
+ "L2:",
+ ".line 25",
+ " iconst_3",
+ " istore 4",
+ "L3:",
+ " .line 27",
+ " iload 3",
+ " istore 5",
+ "L4:",
+ " .line 28",
+ " iload 2",
+ " istore 3",
+ "L5:",
+ " .line 29",
+ " iload 5",
+ " istore 2",
+ "L6:",
+ " .line 32",
+ " new java/lang/StringBuilder",
+ " dup",
+ " invokespecial java/lang/StringBuilder/<init>()V",
+ " ldc \"And the value of y is: \"",
+ " invokevirtual"
+ + " java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " iload 2",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " iload 3",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " iload 4",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;",
+ " astore 5",
+ "L7:",
+ " .line 34",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " aload 5",
+ " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ "L8:",
+ " .line 35",
+ " return",
+ "L9:");
clazz.addMainMethod(
".limit stack 3",
@@ -105,7 +110,7 @@
ClassSubject classSubject = inspector.clazz("Test");
MethodSubject methodSubject = classSubject.method(foo);
DexCode code = methodSubject.getMethod().getCode().asDexCode();
- DexDebugInfo info = code.getDebugInfo();
+ EventBasedDebugInfo info = code.getDebugInfo().asEventBasedInfo();
assertEquals(23, info.startLine);
assertEquals(1, info.parameters.length);
assertEquals("bar", info.parameters[0].toString());
diff --git a/src/test/java/com/android/tools/r8/naming/FieldSharedParentMinificationTest.java b/src/test/java/com/android/tools/r8/naming/FieldSharedParentMinificationTest.java
new file mode 100644
index 0000000..e3a8047
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/FieldSharedParentMinificationTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FieldSharedParentMinificationTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(
+ I.class, J.class, A.class, B.class, C.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("true", "42", "Hello World!")
+ .inspect(
+ inspector -> {
+ FieldSubject foo = inspector.clazz(I.class).uniqueFieldWithName("foo");
+ FieldSubject bar = inspector.clazz(J.class).uniqueFieldWithName("bar");
+ FieldSubject baz = inspector.clazz(A.class).uniqueFieldWithName("baz");
+ assertThat(foo, isPresentAndRenamed());
+ assertThat(bar, isPresentAndRenamed());
+ assertThat(baz, isPresentAndRenamed());
+ Set<String> seenNames =
+ ImmutableSet.of(foo.getFinalName(), bar.getFinalName(), baz.getFinalName());
+ assertEquals(ImmutableSet.of("a", "b", "c"), seenNames);
+ });
+ }
+
+ public interface I {
+
+ int foo = 42;
+ }
+
+ public interface J {
+ String bar = "Hello World!";
+ }
+
+ public static class A {
+
+ boolean baz = System.currentTimeMillis() > 0;
+ }
+
+ public static class B extends A implements I {}
+
+ public static class C extends A implements J {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A().baz);
+ System.out.println(new B().foo);
+ System.out.println(new C().bar);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceFieldMinificationTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceFieldMinificationTest.java
index ca294fa..f72ffa2 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceFieldMinificationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceFieldMinificationTest.java
@@ -8,13 +8,27 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.testclasses.Greeting;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
/** Regression test for b/128600647. */
+@RunWith(Parameterized.class)
public class InterfaceFieldMinificationTest extends TestBase {
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
@Test
public void test() throws Exception {
String expectedOutput = StringUtils.lines("Greeter: Hello world!");
@@ -26,7 +40,8 @@
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
}
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
index 5ed7de6..c09e451 100644
--- a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
@@ -138,7 +138,7 @@
// as this is a release build.
assertTrue(
(code.getDebugInfo() == null)
- || Arrays.stream(code.getDebugInfo().events)
+ || Arrays.stream(code.getDebugInfo().asEventBasedInfo().events)
.allMatch(event -> !(event instanceof StartLocal)));
}
diff --git a/src/test/java/com/android/tools/r8/regress/b147865212/Regress147865212.java b/src/test/java/com/android/tools/r8/regress/b147865212/Regress147865212.java
index d7133e7..fa3661e 100644
--- a/src/test/java/com/android/tools/r8/regress/b147865212/Regress147865212.java
+++ b/src/test/java/com/android/tools/r8/regress/b147865212/Regress147865212.java
@@ -35,7 +35,8 @@
}
private boolean hasLocal(MethodSubject method) {
- return Arrays.stream(method.getMethod().getCode().asDexCode().getDebugInfo().events)
+ return Arrays.stream(
+ method.getMethod().getCode().asDexCode().getDebugInfo().asEventBasedInfo().events)
.anyMatch(event -> event instanceof StartLocal);
}
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
index cc7900e..5405ecb 100644
--- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.regress.b150400371;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,8 +45,9 @@
MethodSubject main =
inspector.method(InlineInto.class.getDeclaredMethod("main", String[].class));
if (parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
+ // Method has 14 actual lines, the PC mapping table will have about 50.
IntSet lines = new IntArraySet(main.getLineNumberTable().getLines());
- assertEquals(2, lines.size());
+ assertTrue(lines.size() > 20);
} else {
assertNull(main.getLineNumberTable());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnTargetedMethodTest.java
index 153bf38..02947f7 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnTargetedMethodTest.java
@@ -8,14 +8,29 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.lang.reflect.Proxy;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class IfOnTargetedMethodTest extends TestBase {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ @Parameter public TestParameters parameters;
+
@Test
public void test() throws Exception {
String expectedOutput = StringUtils.lines("Hello world!");
@@ -23,13 +38,14 @@
testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
CodeInspector inspector =
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
.addInnerClasses(IfOnTargetedMethodTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
"-if interface * { @" + MyAnnotation.class.getTypeName() + " <methods>; }",
"-keep,allowobfuscation interface <1>")
- .run(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 296004d..8644e9a 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -996,7 +996,7 @@
DescriptorUtils.getClassBinaryName(bootstrapMethodHolder),
bootstrapMethodName,
bootstrapMethodSignature,
- false),
+ bootstrapMethodHolder.isInterface()),
new Object[] {}));
} else {
super.visitLdcInsn(value);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 813739b..a41ab17 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -217,16 +218,21 @@
}
if (code.isDexCode()) {
DexCode dexCode = code.asDexCode();
- if (dexCode.getDebugInfo() != null) {
- for (DexString parameter : dexCode.getDebugInfo().parameters) {
- if (parameter != null) {
- return true;
- }
+ if (dexCode.getDebugInfo() == null) {
+ return false;
+ }
+ EventBasedDebugInfo eventBasedInfo = dexCode.getDebugInfo().asEventBasedInfo();
+ if (eventBasedInfo == null) {
+ return false;
+ }
+ for (DexString parameter : eventBasedInfo.parameters) {
+ if (parameter != null) {
+ return true;
}
- for (DexDebugEvent event : dexCode.getDebugInfo().events) {
- if (event instanceof DexDebugEvent.StartLocal) {
- return true;
- }
+ }
+ for (DexDebugEvent event : eventBasedInfo.events) {
+ if (event instanceof DexDebugEvent.StartLocal) {
+ return true;
}
}
return false;
@@ -268,14 +274,14 @@
}
private LineNumberTable getDexLineNumberTable(DexCode code) {
- DexDebugInfo debugInfo = code.getDebugInfo();
- if (debugInfo == null) {
+ EventBasedDebugInfo info = DexDebugInfo.convertToEventBased(code, codeInspector.getFactory());
+ if (info == null) {
return null;
}
Object2IntMap<InstructionSubject> lineNumberTable = new Object2IntOpenHashMap<>();
DexDebugPositionState state =
- new DexDebugPositionState(debugInfo.startLine, getMethod().getReference());
- Iterator<DexDebugEvent> iterator = Arrays.asList(debugInfo.events).iterator();
+ new DexDebugPositionState(info.startLine, getMethod().getReference());
+ Iterator<DexDebugEvent> iterator = Arrays.asList(info.events).iterator();
for (Instruction insn : code.instructions) {
int offset = insn.getOffset();
while (state.getCurrentPc() < offset && iterator.hasNext()) {