blob: 09f740ea6635c5380912d34274e75166fd4998e3 [file] [log] [blame]
// Copyright (c) 2016, 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.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 abstract class DexDebugInfo extends CachedHashValueDexItem
implements StructuralItem<DexDebugInfo> {
private enum DebugInfoKind {
EVENT_BASED,
PC_BASED
}
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
abstract void collectMixedSectionItems(MixedSectionCollection collection);
@Override
public abstract DexDebugInfo self();
@Override
public StructuralMapping<DexDebugInfo> getStructuralMapping() {
throw new Unreachable();
}
@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 internalAcceptCompareTo(other, visitor);
}
@Override
protected final boolean computeEquals(Object other) {
return Equatable.equalsImpl(this, other);
}
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.
}
public static int estimatedWriteSize(int parameterCount, int maxPc) {
return LebUtils.sizeAsUleb128(START_LINE)
+ LebUtils.sizeAsUleb128(parameterCount)
+ parameterCount * LebUtils.sizeAsUleb128(0)
+ 1
+ maxPc
+ 1;
}
@Override
public int estimatedWriteSize() {
return estimatedWriteSize(parameterCount, maxPc);
}
@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);
}
}
@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();
}
}
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);
int pc = 0;
int delta = 0;
for (Instruction instruction : code.instructions) {
if (instruction.canThrow()) {
DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory);
pc += delta;
delta = 0;
}
delta += instruction.getSize();
}
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));
}
public static DexDebugInfoForWriting convertToWritable(DexDebugInfo debugInfo) {
if (debugInfo == null) {
return null;
}
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);
}
}
}