Merge commit '3ac5a16eb35ca70b690c7b85964e397114a921dd' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 74a3d20..068f2ec 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -274,7 +274,7 @@
boolean hasClassResources = appView.appInfo().app().getFlags().hasReadProgramClassFromCf();
boolean hasDexResources = appView.appInfo().app().getFlags().hasReadProgramClassFromDex();
- Marker marker = options.getMarker(Tool.D8);
+ Marker marker = hasClassResources ? options.getMarker(Tool.D8) : null;
timing.time(
"Run inspections",
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 21fccf4..a8dea77 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -283,7 +283,7 @@
new SameNestHost(appView),
new SameParentClass(),
new SyntheticItemsPolicy(appView, mode),
- new RespectPackageBoundaries(appView),
+ new RespectPackageBoundaries(appView, mode),
new NoDifferentApiReferenceLevel(appView),
new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index e3986c8..ded7e1d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -7,11 +7,13 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
@@ -25,9 +27,11 @@
public class RespectPackageBoundaries extends MultiClassPolicy {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final Mode mode;
- public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
this.appView = appView;
+ this.mode = mode;
}
boolean shouldRestrictMergingAcrossPackageBoundary(DexProgramClass clazz) {
@@ -83,7 +87,29 @@
method -> {
boolean foundIllegalAccess =
method.registerCodeReferencesWithResult(
- new IllegalAccessDetector(appView, method));
+ new IllegalAccessDetector(appView, method) {
+
+ @Override
+ protected boolean checkRewrittenFieldType(DexClassAndField field) {
+ if (mode.isInitial()) {
+ // If the type of the field is package private, we need to keep the
+ // current class in its package in case we end up synthesizing a
+ // check-cast for the field type after relaxing the type of the field
+ // after instance field merging.
+ DexType fieldBaseType = field.getType().toBaseType(dexItemFactory());
+ if (fieldBaseType.isClassType()) {
+ DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
+ if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
+ return setFoundPackagePrivateAccess();
+ }
+ }
+ } else {
+ // No relaxing of field types, hence no insertion of casts where we need
+ // to guarantee visibility.
+ }
+ return continueSearchForPackagePrivateAccess();
+ }
+ });
if (foundIllegalAccess) {
return TraversalContinuation.doBreak();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index b17536e..834fd24 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
/**
* Instruction introducing an SSA value with attached local information.
@@ -87,4 +88,9 @@
assert verifyTypesHelper.isAssignable(src().getType(), getOutType());
return true;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addDebugLocalWrite(src());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
index 06d9d8f..6d5083c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
@@ -11,19 +11,24 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Set;
public class AssertionErrorTwoArgsConstructorRewriter {
@@ -41,11 +46,12 @@
if (options.canUseAssertionErrorTwoArgumentConstructor()) {
return;
}
-
+ Set<Value> newOutValues = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
InstructionListIterator insnIterator = block.listIterator(code);
+ List<NewInstance> newInstancesToRemove = new ArrayList<>();
while (insnIterator.hasNext()) {
Instruction current = insnIterator.next();
if (current.isInvokeMethod()) {
@@ -53,24 +59,31 @@
if (invokedMethod == dexItemFactory.assertionErrorMethods.initMessageAndCause) {
List<Value> inValues = current.inValues();
assert inValues.size() == 3; // receiver, message, cause
-
- Value assertionError =
- code.createValue(
- TypeElement.fromDexType(
- dexItemFactory.assertionErrorType,
- Nullability.definitelyNotNull(),
- appView));
- Instruction invoke =
- new InvokeStatic(
- createSynthetic(methodProcessingContext).getReference(),
- assertionError,
- inValues.subList(1, 3));
+ Value newInstanceValue = current.getFirstOperand();
+ Instruction definition = newInstanceValue.getDefinition();
+ if (!definition.isNewInstance()) {
+ continue;
+ }
+ InvokeStatic invoke =
+ InvokeStatic.builder()
+ .setMethod(createSynthetic(methodProcessingContext).getReference())
+ .setFreshOutValue(
+ code, dexItemFactory.assertionErrorType.toTypeElement(appView))
+ .setPosition(current)
+ .setArguments(inValues.subList(1, 3))
+ .build();
insnIterator.replaceCurrentInstruction(invoke);
- inValues.get(0).replaceUsers(assertionError);
- inValues.get(0).definition.removeOrReplaceByDebugLocalRead(code);
+ newInstanceValue.replaceUsers(invoke.outValue());
+ newOutValues.add(invoke.outValue());
+ newInstancesToRemove.add(definition.asNewInstance());
}
}
}
+ newInstancesToRemove.forEach(
+ newInstance -> newInstance.removeOrReplaceByDebugLocalRead(code));
+ }
+ if (!newOutValues.isEmpty()) {
+ new TypeAnalysis(appView).widening(newOutValues);
}
assert code.isConsistentSSA(appView);
}
@@ -103,6 +116,15 @@
factory, methodSig)));
synchronized (synthesizedMethods) {
synthesizedMethods.add(method);
+ OptimizationFeedback.getSimpleFeedback()
+ .setDynamicReturnType(
+ method,
+ appView,
+ DynamicType.createExact(
+ dexItemFactory
+ .assertionErrorType
+ .toTypeElement(appView, Nullability.definitelyNotNull())
+ .asClassType()));
}
return method;
}
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
index 4e148ee..9bdc15a 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -27,35 +27,45 @@
public class IR2LIRConverter {
- private IR2LIRConverter() {}
+ private final DexItemFactory factory;
+ private final IRCode irCode;
+ private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+ private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+ private final LIRBuilder<Value, BasicBlock> builder;
- public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
- irCode.traceBlocks();
- Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
- Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
- int instructionIndex = 0;
- int valueIndex = 0;
- for (BasicBlock block : irCode.blocks) {
- blocks.put(block, instructionIndex);
- for (Phi phi : block.getPhis()) {
- values.put(phi, valueIndex);
- valueIndex++;
- instructionIndex++;
- }
- for (Instruction instruction : block.getInstructions()) {
- if (instruction.hasOutValue()) {
- values.put(instruction.outValue(), valueIndex);
- }
- valueIndex++;
- if (!instruction.isArgument()) {
- instructionIndex++;
- }
- }
- }
- LIRBuilder<Value, BasicBlock> builder =
+ private IR2LIRConverter(DexItemFactory factory, IRCode irCode) {
+ this.factory = factory;
+ this.irCode = irCode;
+ this.builder =
new LIRBuilder<Value, BasicBlock>(
irCode.context().getReference(), values::getInt, blocks::getInt, factory)
.setMetadata(irCode.metadata());
+ }
+
+ public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
+ return new IR2LIRConverter(factory, irCode).internalTranslate();
+ }
+
+ private void recordBlock(BasicBlock block, int blockIndex) {
+ blocks.put(block, blockIndex);
+ }
+
+ private void recordValue(Value value, int valueIndex) {
+ values.put(value, valueIndex);
+ if (value.hasLocalInfo()) {
+ builder.setDebugValue(value.getLocalInfo(), valueIndex);
+ }
+ }
+
+ private LIRCode internalTranslate() {
+ irCode.traceBlocks();
+ computeBlockAndValueTables();
+ computeInstructions();
+ return builder.build();
+ }
+
+ private void computeInstructions() {
+ int currentInstructionIndex = 0;
BasicBlockIterator blockIt = irCode.listIterator();
while (blockIt.hasNext()) {
BasicBlock block = blockIt.next();
@@ -67,6 +77,7 @@
for (Phi phi : block.getPhis()) {
permuteOperands(phi.getOperands(), permutation, operands);
builder.addPhi(phi.getType(), Arrays.asList(operands));
+ currentInstructionIndex++;
}
}
if (block.hasCatchHandlers()) {
@@ -79,20 +90,47 @@
InstructionIterator it = block.iterator();
while (it.hasNext()) {
Instruction instruction = it.next();
+ assert !instruction.hasOutValue()
+ || currentInstructionIndex == values.getInt(instruction.outValue());
builder.setCurrentPosition(instruction.getPosition());
+ if (!instruction.getDebugValues().isEmpty()) {
+ builder.setDebugLocalEnds(currentInstructionIndex, instruction.getDebugValues());
+ }
+
if (instruction.isGoto()) {
BasicBlock nextBlock = blockIt.peekNext();
if (instruction.asGoto().getTarget() == nextBlock) {
builder.addFallthrough();
- } else {
- instruction.buildLIR(builder);
+ currentInstructionIndex++;
+ continue;
}
- } else {
- instruction.buildLIR(builder);
+ }
+ instruction.buildLIR(builder);
+ currentInstructionIndex++;
+ }
+ }
+ }
+
+ private void computeBlockAndValueTables() {
+ int instructionIndex = 0;
+ int valueIndex = 0;
+ for (BasicBlock block : irCode.blocks) {
+ recordBlock(block, instructionIndex);
+ for (Phi phi : block.getPhis()) {
+ recordValue(phi, valueIndex);
+ valueIndex++;
+ instructionIndex++;
+ }
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.hasOutValue()) {
+ recordValue(instruction.outValue(), valueIndex);
+ }
+ valueIndex++;
+ if (!instruction.isArgument()) {
+ instructionIndex++;
}
}
}
- return builder.build();
}
private static void permuteOperands(List<Value> operands, int[] permutation, Value[] output) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
index 056129b..28547c0 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DebugLocalWrite;
import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.Div;
import com.android.tools.r8.ir.code.Goto;
@@ -192,18 +193,21 @@
return arguments;
}
+ public int toInstructionIndexInIR(int lirIndex) {
+ return lirIndex + code.getArgumentCount();
+ }
+
public int peekNextInstructionIndex() {
return nextInstructionIndex;
}
public Value getOutValueForNextInstruction(TypeElement type) {
- // TODO(b/225838009): Support debug locals.
- DebugLocalInfo localInfo = null;
- int index = peekNextInstructionIndex() + code.getArgumentCount();
- Value value = values[index];
+ int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
+ DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
+ Value value = values[valueIndex];
if (value == null) {
value = new Value(valueNumberGenerator.next(), type, localInfo);
- values[index] = value;
+ values[valueIndex] = value;
} else {
value.setType(type);
if (localInfo != null) {
@@ -214,17 +218,17 @@
}
public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
- int index = peekNextInstructionIndex() + code.getArgumentCount();
+ int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
+ DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
// TODO(b/225838009): The phi constructor implicitly adds to the block, so we need to ensure
// the block. However, we must grab the index above. Find a way to clean this up so it is
// uniform with instructions.
advanceInstructionState();
// Creating the phi implicitly adds it to currentBlock.
- DebugLocalInfo localInfo = null;
Phi phi =
new Phi(
valueNumberGenerator.next(), currentBlock, type, localInfo, RegisterReadType.NORMAL);
- Value value = values[index];
+ Value value = values[valueIndex];
if (value != null) {
// A fake ssa value has already been created, replace the users by the actual phi.
// TODO(b/225838009): We could consider encoding the value type as a bit in the value index
@@ -232,7 +236,7 @@
assert !value.isPhi();
value.replaceUsers(phi);
}
- values[index] = phi;
+ values[valueIndex] = phi;
return phi;
}
@@ -243,10 +247,18 @@
}
private void addInstruction(Instruction instruction) {
+ int index = toInstructionIndexInIR(peekNextInstructionIndex());
advanceInstructionState();
instruction.setPosition(currentPosition);
currentBlock.getInstructions().add(instruction);
instruction.setBlock(currentBlock);
+ int[] debugEndIndices = code.getDebugLocalEnds(index);
+ if (debugEndIndices != null) {
+ for (int debugEndIndex : debugEndIndices) {
+ Value debugValue = getValue(debugEndIndex);
+ debugValue.addDebugLocalEnd(instruction);
+ }
+ }
}
private void addThisArgument(DexType type) {
@@ -259,6 +271,10 @@
// which would otherwise advance the state.
Value dest = getValue(index);
dest.setType(type.toTypeElement(appView));
+ DebugLocalInfo localInfo = code.getDebugLocalInfo(index);
+ if (localInfo != null) {
+ dest.setLocalInfo(localInfo);
+ }
Argument argument = new Argument(dest, index, type.isBooleanType());
assert currentBlock != null;
assert currentPosition.isSyntheticPosition();
@@ -380,5 +396,15 @@
Value dest = getOutValueForNextInstruction(exceptionType.toTypeElement(appView));
addInstruction(new MoveException(dest, exceptionType, appView.options()));
}
+
+ @Override
+ public void onDebugLocalWrite(int srcIndex) {
+ Value src = getValue(srcIndex);
+ // The type is in the local table, so initialize it with bottom and reset with the local info.
+ Value dest = getOutValueForNextInstruction(TypeElement.getBottom());
+ TypeElement type = dest.getLocalInfo().type.toTypeElement(appView);
+ dest.setType(type);
+ addInstruction(new DebugLocalWrite(dest, src));
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
index 2971d2e..44876ed 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
@@ -19,6 +20,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.lightir.LIRCode.DebugLocalInfoTable;
import com.android.tools.r8.lightir.LIRCode.PositionEntry;
import com.android.tools.r8.lightir.LIRCode.TryCatchTable;
import com.android.tools.r8.utils.ListUtils;
@@ -29,7 +31,9 @@
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
* Builder for constructing LIR code from IR.
@@ -68,6 +72,11 @@
private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchRanges =
new Int2ReferenceOpenHashMap<>();
+ // Mapping from SSA value definition to the local name index in the constant pool.
+ private final Int2ReferenceMap<DebugLocalInfo> debugLocals = new Int2ReferenceOpenHashMap<>();
+ // Mapping from instruction to the end usage of SSA values with debug local info.
+ private final Int2ReferenceMap<int[]> debugLocalEnds = new Int2ReferenceOpenHashMap();
+
// TODO(b/225838009): Reconsider this fixed space as the operand count for phis is much larger.
// Pre-allocated space for caching value indexes when writing instructions.
private static final int MAX_VALUE_COUNT = 10;
@@ -87,6 +96,16 @@
flushedPosition = currentPosition;
}
+ public DexType toDexType(TypeElement typeElement) {
+ if (typeElement.isPrimitiveType()) {
+ return typeElement.asPrimitiveType().toDexType(factory);
+ }
+ if (typeElement.isReferenceType()) {
+ return typeElement.asReferenceType().toDexType(factory);
+ }
+ throw new Unreachable("Unexpected type element: " + typeElement);
+ }
+
public void addTryCatchHanders(int blockIndex, CatchHandlers<Integer> handlers) {
tryCatchRanges.put(blockIndex, handlers);
}
@@ -149,6 +168,23 @@
return this;
}
+ public LIRBuilder<V, B> setDebugValue(DebugLocalInfo debugInfo, int valueIndex) {
+ DebugLocalInfo old = debugLocals.put(valueIndex, debugInfo);
+ assert old == null;
+ return this;
+ }
+
+ public LIRBuilder<V, B> setDebugLocalEnds(int instructionIndex, Set<V> endValues) {
+ int size = endValues.size();
+ int[] indices = new int[size];
+ Iterator<V> iterator = endValues.iterator();
+ for (int i = 0; i < size; i++) {
+ indices[i] = getValueIndex(iterator.next());
+ }
+ debugLocalEnds.put(instructionIndex, indices);
+ return this;
+ }
+
public LIRBuilder<V, B> addArgument(int index, boolean knownToBeBoolean) {
// Arguments are implicitly given by method descriptor and not an actual instruction.
assert argumentCount == index;
@@ -387,18 +423,21 @@
}
public LIRBuilder<V, B> addPhi(TypeElement type, List<V> operands) {
- DexType dexType =
- type.isPrimitiveType()
- ? type.asPrimitiveType().toDexType(factory)
- : type.asReferenceType().toDexType(factory);
+ DexType dexType = toDexType(type);
return addInstructionTemplate(LIROpcodes.PHI, Collections.singletonList(dexType), operands);
}
+ public LIRBuilder<V, B> addDebugLocalWrite(V src) {
+ return addOneValueInstruction(LIROpcodes.DEBUGLOCALWRITE, src);
+ }
+
public LIRCode build() {
assert metadata != null;
int constantsCount = constants.size();
DexItem[] constantTable = new DexItem[constantsCount];
constants.forEach((item, index) -> constantTable[index] = item);
+ DebugLocalInfoTable debugTable =
+ debugLocals.isEmpty() ? null : new DebugLocalInfoTable(debugLocals, debugLocalEnds);
return new LIRCode(
metadata,
constantTable,
@@ -406,6 +445,7 @@
argumentCount,
byteWriter.toByteArray(),
instructionCount,
- new TryCatchTable(tryCatchRanges));
+ new TryCatchTable(tryCatchRanges),
+ debugTable);
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
index 8caa8d6..09619f4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -40,6 +41,20 @@
}
}
+ public static class DebugLocalInfoTable {
+ private final Int2ReferenceMap<DebugLocalInfo> valueToLocalMap;
+ private final Int2ReferenceMap<int[]> instructionToEndUseMap;
+
+ public DebugLocalInfoTable(
+ Int2ReferenceMap<DebugLocalInfo> valueToLocalMap,
+ Int2ReferenceMap<int[]> instructionToEndUseMap) {
+ assert !valueToLocalMap.isEmpty();
+ assert !instructionToEndUseMap.isEmpty();
+ this.valueToLocalMap = valueToLocalMap;
+ this.instructionToEndUseMap = instructionToEndUseMap;
+ }
+ }
+
private final IRMetadata metadata;
/** Constant pool of items. */
@@ -59,6 +74,9 @@
/** Table of try-catch handlers for each basic block. */
private final TryCatchTable tryCatchTable;
+ /** Table of debug local information for each SSA value (if present). */
+ private final DebugLocalInfoTable debugLocalInfoTable;
+
public static <V, B> LIRBuilder<V, B> builder(
DexMethod method,
ValueIndexGetter<V> valueIndexGetter,
@@ -75,7 +93,8 @@
int argumentCount,
byte[] instructions,
int instructionCount,
- TryCatchTable tryCatchTable) {
+ TryCatchTable tryCatchTable,
+ DebugLocalInfoTable debugLocalInfoTable) {
this.metadata = metadata;
this.constants = constants;
this.positionTable = positions;
@@ -83,6 +102,7 @@
this.instructions = instructions;
this.instructionCount = instructionCount;
this.tryCatchTable = tryCatchTable;
+ this.debugLocalInfoTable = debugLocalInfoTable;
}
public int getArgumentCount() {
@@ -113,6 +133,20 @@
return tryCatchTable;
}
+ public DebugLocalInfoTable getDebugLocalInfoTable() {
+ return debugLocalInfoTable;
+ }
+
+ public DebugLocalInfo getDebugLocalInfo(int valueIndex) {
+ return debugLocalInfoTable == null ? null : debugLocalInfoTable.valueToLocalMap.get(valueIndex);
+ }
+
+ public int[] getDebugLocalEnds(int instructionIndex) {
+ return debugLocalInfoTable == null
+ ? null
+ : debugLocalInfoTable.instructionToEndUseMap.get(instructionIndex);
+ }
+
@Override
public LIRIterator iterator() {
return new LIRIterator(new ByteArrayIterator(instructions));
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
index 0b0dfc8..abfea89 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -186,6 +186,7 @@
int PHI = 206;
int FALLTHROUGH = 207;
int MOVEEXCEPTION = 208;
+ int DEBUGLOCALWRITE = 209;
static String toString(int opcode) {
switch (opcode) {
@@ -498,6 +499,8 @@
return "FALLTHROUGH";
case MOVEEXCEPTION:
return "MOVEEXCEPTION";
+ case DEBUGLOCALWRITE:
+ return "DEBUGLOCALWRITE";
default:
throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
index 5c87faf..f0bc3f3 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -57,6 +57,8 @@
public void onMoveException(DexType exceptionType) {}
+ public void onDebugLocalWrite(int srcIndex) {}
+
public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
public void onInvokeDirect(DexMethod method, IntList arguments) {
@@ -199,6 +201,12 @@
onMoveException(type);
return;
}
+ case LIROpcodes.DEBUGLOCALWRITE:
+ {
+ int srcIndex = view.getNextValueOperand();
+ onDebugLocalWrite(srcIndex);
+ return;
+ }
default:
throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(opcode));
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index 9a74b4e..bf9123e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -20,7 +20,7 @@
// that allow for retracing classes in the form <class>: lorem ipsum...
// Seems like Proguard retrace is expecting the form "Caused by: <class>".
public static final String DEFAULT_REGULAR_EXPRESSION =
- "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%S\\)\\s*(?:~\\[.*\\])?)"
+ "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%S\\)\\p{Z}*(?:~\\[.*\\])?)"
+ "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
private final Pattern compiledPattern;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index bc1ce29..b479808 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
@@ -2106,8 +2107,8 @@
}
}
- // Searches for a reference to a non-public class, field or method declared in the same package
- // as [source].
+ // Searches for a reference to a non-private, non-public class, field or method declared in the
+ // same package as [source].
public static class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
@@ -2118,106 +2119,144 @@
this.appView = appView;
}
- private boolean checkFieldReference(DexField field) {
- DexType baseType =
- appView.graphLens().lookupType(field.holder.toBaseType(appView.dexItemFactory()));
- if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
- if (checkTypeReference(field.holder) || checkTypeReference(field.type)) {
- return true;
- }
+ protected boolean checkFoundPackagePrivateAccess() {
+ assert getResult();
+ return true;
+ }
- DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField();
- if (definition == null || !definition.accessFlags.isPublic()) {
- setResult(true);
- return true;
- }
- }
+ protected boolean setFoundPackagePrivateAccess() {
+ setResult(true);
+ return true;
+ }
+
+ protected static boolean continueSearchForPackagePrivateAccess() {
return false;
}
- private boolean checkMethodReference(DexMethod method, OptionalBool isInterface) {
- DexType baseType =
- appView.graphLens().lookupType(method.holder.toBaseType(appView.dexItemFactory()));
- if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
- if (checkTypeReference(method.holder)
- || checkTypeReference(method.proto.returnType)
- || Iterables.any(method.getParameters(), this::checkTypeReference)) {
- return true;
- }
+ private boolean checkFieldReference(DexField field) {
+ return checkRewrittenFieldReference(appView.graphLens().lookupField(field));
+ }
- MethodResolutionResult resolutionResult =
- isInterface.isUnknown()
- ? appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method)
- : appView.appInfo().resolveMethodLegacy(method, isInterface.isTrue());
- if (!resolutionResult.isSingleResolution()
- || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) {
- setResult(true);
- return true;
+ private boolean checkRewrittenFieldReference(DexField field) {
+ assert field.getHolderType().isClassType();
+ DexType fieldHolder = field.getHolderType();
+ if (fieldHolder.isSamePackage(getContext().getHolderType())) {
+ if (checkRewrittenTypeReference(fieldHolder)) {
+ return checkFoundPackagePrivateAccess();
+ }
+ DexClassAndField resolvedField = appView.appInfo().resolveField(field).getResolutionPair();
+ if (resolvedField == null) {
+ return setFoundPackagePrivateAccess();
+ }
+ if (resolvedField.getHolder() != getContext().getHolder()
+ && !resolvedField.getAccessFlags().isPublic()) {
+ return setFoundPackagePrivateAccess();
+ }
+ if (checkRewrittenFieldType(resolvedField)) {
+ return checkFoundPackagePrivateAccess();
}
}
- return false;
+ return continueSearchForPackagePrivateAccess();
+ }
+
+ protected boolean checkRewrittenFieldType(DexClassAndField field) {
+ return continueSearchForPackagePrivateAccess();
+ }
+
+ private boolean checkRewrittenMethodReference(
+ DexMethod rewrittenMethod, OptionalBool isInterface) {
+ DexType baseType = rewrittenMethod.getHolderType().toBaseType(appView.dexItemFactory());
+ if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
+ if (checkTypeReference(rewrittenMethod.getHolderType())) {
+ return checkFoundPackagePrivateAccess();
+ }
+ MethodResolutionResult resolutionResult =
+ isInterface.isUnknown()
+ ? appView.appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod)
+ : appView.appInfo().resolveMethod(rewrittenMethod, isInterface.isTrue());
+ if (!resolutionResult.isSingleResolution()) {
+ return setFoundPackagePrivateAccess();
+ }
+ DexClassAndMethod resolvedMethod =
+ resolutionResult.asSingleResolution().getResolutionPair();
+ if (resolvedMethod.getHolder() != getContext().getHolder()
+ && !resolvedMethod.getAccessFlags().isPublic()) {
+ return setFoundPackagePrivateAccess();
+ }
+ }
+ return continueSearchForPackagePrivateAccess();
}
private boolean checkTypeReference(DexType type) {
- DexType baseType = appView.graphLens().lookupType(type.toBaseType(appView.dexItemFactory()));
+ return internalCheckTypeReference(type, appView.graphLens());
+ }
+
+ private boolean checkRewrittenTypeReference(DexType type) {
+ return internalCheckTypeReference(type, GraphLens.getIdentityLens());
+ }
+
+ private boolean internalCheckTypeReference(DexType type, GraphLens graphLens) {
+ DexType baseType = graphLens.lookupType(type.toBaseType(appView.dexItemFactory()));
if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
DexClass clazz = appView.definitionFor(baseType);
- if (clazz == null || !clazz.accessFlags.isPublic()) {
- setResult(true);
- return true;
+ if (clazz == null || !clazz.isPublic()) {
+ return setFoundPackagePrivateAccess();
}
}
- return false;
+ return continueSearchForPackagePrivateAccess();
}
@Override
public void registerInitClass(DexType clazz) {
- checkTypeReference(clazz);
+ if (appView.initClassLens().isFinal()) {
+ // The InitClass lens is always rewritten up until the most recent graph lens, so first map
+ // the class type to the most recent graph lens.
+ DexType rewrittenType = appView.graphLens().lookupType(clazz);
+ DexField initClassField = appView.initClassLens().getInitClassField(rewrittenType);
+ checkRewrittenFieldReference(initClassField);
+ } else {
+ checkTypeReference(clazz);
+ }
}
@Override
public void registerInvokeVirtual(DexMethod method) {
- MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), VIRTUAL);
- checkMethodReference(lookup.getReference(), OptionalBool.FALSE);
+ MethodLookupResult lookup = appView.graphLens().lookupInvokeVirtual(method, getContext());
+ checkRewrittenMethodReference(lookup.getReference(), OptionalBool.FALSE);
}
@Override
public void registerInvokeDirect(DexMethod method) {
- MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), DIRECT);
- checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+ MethodLookupResult lookup = appView.graphLens().lookupInvokeDirect(method, getContext());
+ checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeStatic(DexMethod method) {
- MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), Type.STATIC);
- checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+ MethodLookupResult lookup = appView.graphLens().lookupInvokeStatic(method, getContext());
+ checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeInterface(DexMethod method) {
- MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), Type.INTERFACE);
- checkMethodReference(lookup.getReference(), OptionalBool.TRUE);
+ MethodLookupResult lookup = appView.graphLens().lookupInvokeInterface(method, getContext());
+ checkRewrittenMethodReference(lookup.getReference(), OptionalBool.TRUE);
}
@Override
public void registerInvokeSuper(DexMethod method) {
- MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), Type.SUPER);
- checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+ MethodLookupResult lookup = appView.graphLens().lookupInvokeSuper(method, getContext());
+ checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInstanceFieldWrite(DexField field) {
- checkFieldReference(appView.graphLens().lookupField(field));
+ checkFieldReference(field);
}
@Override
public void registerInstanceFieldRead(DexField field) {
- checkFieldReference(appView.graphLens().lookupField(field));
+ checkFieldReference(field);
}
@Override
@@ -2227,12 +2266,12 @@
@Override
public void registerStaticFieldRead(DexField field) {
- checkFieldReference(appView.graphLens().lookupField(field));
+ checkFieldReference(field);
}
@Override
public void registerStaticFieldWrite(DexField field) {
- checkFieldReference(appView.graphLens().lookupField(field));
+ checkFieldReference(field);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
index ed2130d..af9f880 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
@@ -47,16 +47,13 @@
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- inspector ->
- inspector.assertMergedInto(
- Reference.classFromClass(A.class),
- Reference.classFromDescriptor(NEW_B_DESCRIPTOR)))
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ .assertSuccessWithOutputLines("Hello, world!");
}
private List<byte[]> getProgramClassFileData() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index e17ac14..0370c29 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -29,7 +28,8 @@
/**
* Simple test that compiles framework.jar with D8 a number of times with various number of threads
- * available to the compiler. This test also tests the hidden marker inserted into classes.dex.
+ * available to the compiler. This test also tests that no hidden marker is inserted into
+ * classes.dex.
*/
@RunWith(Parameterized.class)
public class D8FrameworkDexPassthroughMarkerTest {
@@ -62,15 +62,10 @@
Marker marker = new Marker(Tool.D8)
.setVersion("1.0.0")
.setMinApi(minApi);
- InternalOptions options = new InternalOptions();
- DexString markerDexString = options.itemFactory.createString(marker.toString());
- Marker selfie = Marker.parse(markerDexString);
- assert marker.equals(selfie);
AndroidApp app = ToolHelper.runD8(command, opts -> opts.setMarker(marker));
- DexApplication dexApp = new ApplicationReader(app, options, Timing.empty()).read();
+ DexApplication dexApp =
+ new ApplicationReader(app, new InternalOptions(), Timing.empty()).read();
Collection<Marker> markers = dexApp.dexItemFactory.extractMarkers();
- assertEquals(1, markers.size());
- Marker readMarker = markers.iterator().next();
- assertEquals(marker, readMarker);
+ assertEquals(0, markers.size());
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index dbeb66b..c78212b 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -40,6 +40,7 @@
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import org.junit.After;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -191,6 +192,7 @@
* Validates that when all protos are kept and the proto optmization is enabled, the generated
* proto schemas are identical to the proto schemas in the input.
*/
+ @Ignore
@Test
public void testProtoRewriting() throws Exception {
assumeTrue(shouldRunSlowTests());
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 907ca27..483f18b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.kotlin.TestKotlinClass.AccessorKind;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -219,7 +220,9 @@
runTest(PROPERTIES_PACKAGE_NAME, mainClass)
.inspect(
inspector -> {
- if (allowAccessModification) {
+ if (allowAccessModification
+ || (testParameters.isDexRuntime()
+ && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
checkClassIsRemoved(inspector, testedClass.getOuterClassName());
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index 59ba99b..527e568 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -509,7 +510,9 @@
inspector -> {
checkClassIsRemoved(inspector, testedClass.getClassName());
- if (allowAccessModification) {
+ if (allowAccessModification
+ || (testParameters.isDexRuntime()
+ && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
checkClassIsRemoved(inspector, testedClass.getOuterClassName());
return;
}
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
index 1a8d281..2188ba6 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -5,17 +5,17 @@
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.debug.DebugTestBase;
+import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class LIRRoundtripTest extends TestBase {
+public class LIRRoundtripTest extends DebugTestBase {
static class TestClass {
public static void main(String[] args) {
@@ -64,9 +64,8 @@
.assertSuccessWithOutputLines("Hello, world!");
}
- // TODO(b/225838009): Support debug local info.
- @Test(expected = CompilationFailedException.class)
- public void testRoundtripDebug() throws Exception {
+ @Test
+ public void testRoundtripDebug() throws Throwable {
testForD8(parameters.getBackend())
.debug()
.setMinApi(AndroidApiLevel.B)
@@ -77,6 +76,17 @@
o.testing.roundtripThroughLIR = true;
})
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world!");
+ .assertSuccessWithOutputLines("Hello, world!")
+ .debugger(this::runDebugger);
+ }
+
+ private void runDebugger(DebugTestConfig config) throws Throwable {
+ runDebugTest(
+ config,
+ TestClass.class,
+ breakOnException(typeName(TestClass.class), "main", true, true),
+ run(),
+ checkLocals("args", "message"),
+ run());
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index c62aa08..d60500f 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -74,6 +74,7 @@
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodStackTrace;
import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodWithInliningStackTrace;
+import com.android.tools.r8.retrace.stacktraces.TrailingWhitespaceStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.retrace.stacktraces.VerboseUnknownStackTrace;
@@ -439,6 +440,11 @@
DiagnosticsMatcher.diagnosticMessage(containsString("99.0")));
}
+ @Test
+ public void testTrailingWhitespaceStackTrace() throws Exception {
+ runRetraceTest(new TrailingWhitespaceStackTrace());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java
new file mode 100644
index 0000000..bfc7476
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java
@@ -0,0 +1,49 @@
+// 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class TrailingWhitespaceStackTrace implements StackTraceForTest {
+
+ private static final String NO_BREAKING_SPACE_STRING = new String(new char[] {160});
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat a.b.main(Main.dummy:1)" + NO_BREAKING_SPACE_STRING);
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)"
+ + NO_BREAKING_SPACE_STRING);
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:7)"
+ + NO_BREAKING_SPACE_STRING);
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.naming.retrace.Main -> a.b:",
+ " 1:1:void main(java.lang.String[]):7:7 -> main");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index a376018..3900ffc 100755
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -85,10 +85,23 @@
return cmd
def capture_app_profile_data(app_id, device_id=None):
- cmd = create_adb_cmd(
- 'shell killall -s SIGUSR1 %s' % app_id, device_id)
- subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
- time.sleep(5)
+ ps_cmd = create_adb_cmd('shell ps -o NAME', device_id)
+ stdout = subprocess.check_output(ps_cmd).decode('utf-8').strip()
+ killed_any = False
+ for process_name in stdout.splitlines():
+ if process_name.startswith(app_id):
+ print('Flushing profile for process %s' % process_name)
+ killall_cmd = create_adb_cmd(
+ 'shell killall -s SIGUSR1 %s' % process_name, device_id)
+ killall_result = subprocess.run(killall_cmd, capture_output=True)
+ stdout = killall_result.stdout.decode('utf-8')
+ stderr = killall_result.stderr.decode('utf-8')
+ if killall_result.returncode == 0:
+ killed_any = True
+ else:
+ print('Error: stdout: %s, stderr: %s' % (stdout, stderr))
+ time.sleep(5)
+ assert killed_any, 'Expected to find at least one process'
def check_app_has_profile_data(app_id, device_id=None):
profile_path = get_profile_path(app_id)