summaryrefslogtreecommitdiffstats
path: root/dx/src/com/android/dx
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx')
-rw-r--r--dx/src/com/android/dx/dex/code/LocalEnd.java7
-rw-r--r--dx/src/com/android/dx/dex/code/LocalList.java947
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoDecoder.java340
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoEncoder.java251
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoItem.java40
-rw-r--r--dx/src/com/android/dx/rop/code/LocalItem.java13
-rw-r--r--dx/src/com/android/dx/rop/code/RegisterSpec.java114
-rw-r--r--dx/src/com/android/dx/rop/code/RegisterSpecSet.java57
8 files changed, 559 insertions, 1210 deletions
diff --git a/dx/src/com/android/dx/dex/code/LocalEnd.java b/dx/src/com/android/dx/dex/code/LocalEnd.java
index c19a8dcec..87934dbd4 100644
--- a/dx/src/com/android/dx/dex/code/LocalEnd.java
+++ b/dx/src/com/android/dx/dex/code/LocalEnd.java
@@ -29,10 +29,9 @@ import com.android.dx.rop.code.SourcePosition;
public final class LocalEnd extends ZeroSizeInsn {
/**
* non-null; register spec representing the local variable ended
- * by this instance. <b>Note:</b> Technically, only the register
- * number needs to be recorded here as the rest of the information
- * is implicit in the ambient local variable state, but other code
- * will check the other info for consistency.
+ * by this instance. <b>Note:</b> The only salient part of the spec
+ * is the register number; the rest of the info may be useful for
+ * debugging but shouldn't affect any actual processing
*/
private final RegisterSpec local;
diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java
index 4614fc41e..d963fca08 100644
--- a/dx/src/com/android/dx/dex/code/LocalList.java
+++ b/dx/src/com/android/dx/dex/code/LocalList.java
@@ -23,9 +23,7 @@ import com.android.dx.rop.cst.CstUtf8;
import com.android.dx.rop.type.Type;
import com.android.dx.util.FixedSizeList;
-import java.io.PrintStream;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* List of local variables. Each local variable entry indicates a
@@ -36,9 +34,174 @@ public final class LocalList extends FixedSizeList {
/** non-null; empty instance */
public static final LocalList EMPTY = new LocalList(0);
- /** whether to run the self-check code */
- private static final boolean DEBUG = false;
-
+ /**
+ * Constructs an instance for the given method, based on the given
+ * block order and intermediate local information.
+ *
+ * @param insns non-null; instructions to convert
+ * @return non-null; the constructed list
+ */
+ public static LocalList make(DalvInsnList insns) {
+ ArrayList<Entry> result = new ArrayList<Entry>(100);
+ int codeSize = insns.codeSize();
+ int sz = insns.size();
+ RegisterSpecSet state = null;
+ int stateMax = 0;
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = insns.get(i);
+
+ if (insn instanceof LocalSnapshot) {
+ RegisterSpecSet newState = ((LocalSnapshot) insn).getLocals();
+ boolean first = (state == null);
+
+ if (first) {
+ stateMax = newState.getMaxSize();
+ }
+
+ for (int j = 0; j < stateMax; j++) {
+ RegisterSpec oldSpec = first ? null : state.get(j);
+ RegisterSpec newSpec = newState.get(j);
+ boolean oldEnds = false;
+ boolean newStarts = false;
+
+ if (oldSpec == null) {
+ if (newSpec != null) {
+ /*
+ * This is a newly-introduced local, not
+ * replacing an existing local.
+ */
+ newStarts = true;
+ }
+ } else if (newSpec == null) {
+ /*
+ * This is a local going out of scope, with no
+ * replacement.
+ */
+ oldEnds = true;
+ } else if (!oldSpec.equals(newSpec)) {
+ /*
+ * This is a local going out of scope, immediately
+ * replaced by a different local.
+ */
+ oldEnds = true;
+ newStarts = true;
+ }
+
+ if (oldEnds) {
+ endScope(result, oldSpec, insn.getAddress());
+ }
+
+ if (newStarts) {
+ startScope(result, newSpec, insn.getAddress(),
+ codeSize);
+ }
+ }
+
+ state = newState;
+ } else if (insn instanceof LocalStart) {
+ RegisterSpec newSpec = ((LocalStart) insn).getLocal();
+ RegisterSpec oldSpec = state.get(newSpec);
+
+ boolean oldEnds = false;
+ boolean newStarts = false;
+
+ if (oldSpec == null) {
+ /*
+ * This is a newly-introduced local, not replacing an
+ * existing local.
+ */
+ newStarts = true;
+ } else if (!oldSpec.equals(newSpec)) {
+ /*
+ * This is a local going out of scope, immediately
+ * replaced by a different local.
+ */
+ oldEnds = true;
+ newStarts = true;
+ }
+
+ if (newStarts) {
+ int address = insn.getAddress();
+
+ if (oldEnds) {
+ endScope(result, oldSpec, address);
+ }
+
+ startScope(result, newSpec, address, codeSize);
+
+ if (state.isImmutable()) {
+ state = state.mutableCopy();
+ }
+
+ state.put(newSpec);
+ }
+ }
+ }
+
+ int resultSz = result.size();
+
+ if (resultSz == 0) {
+ return EMPTY;
+ }
+
+ LocalList resultList = new LocalList(resultSz);
+
+ for (int i = 0; i < resultSz; i++) {
+ resultList.set(i, result.get(i));
+ }
+
+ resultList.setImmutable();
+ return resultList;
+ }
+
+ /**
+ * Helper for {@link #make}, to indicate that the given variable has
+ * been introduced.
+ *
+ * @param result non-null; result in-progress
+ * @param spec non-null; register spec for the variable in question
+ * @param startAddress &gt;= 0; address at which the scope starts
+ * (inclusive)
+ * @param endAddress &gt; startAddress; initial scope end address
+ * (exclusive)
+ */
+ private static void startScope(ArrayList<Entry> result, RegisterSpec spec,
+ int startAddress, int endAddress) {
+ result.add(new Entry(startAddress, endAddress, spec));
+ }
+
+ /**
+ * Helper for {@link #make}, to indicate that the given variable's
+ * scope has closed.
+ *
+ * @param result non-null; result in-progress
+ * @param spec non-null; register spec for the variable in question
+ * @param endAddress &gt;= 0; address at which the scope ends (exclusive)
+ */
+ private static void endScope(ArrayList<Entry> result, RegisterSpec spec,
+ int endAddress) {
+ int sz = result.size();
+
+ for (int i = sz - 1; i >= 0; i--) {
+ Entry e = result.get(i);
+ if (e.matches(spec)) {
+ if (e.getStart() == endAddress) {
+ /*
+ * It turns out that the indicated entry doesn't actually
+ * cover any code.
+ */
+ result.remove(i);
+ } else {
+ result.set(i, e.withEnd(endAddress));
+ }
+ return;
+ }
+ }
+
+ throw new RuntimeException("unmatched variable: " + spec);
+ }
+
/**
* Constructs an instance. All indices initially contain <code>null</code>.
*
@@ -64,86 +227,54 @@ public final class LocalList extends FixedSizeList {
* Sets the entry at the given index.
*
* @param n &gt;= 0, &lt; size(); which index
- * @param entry non-null; the entry to set at <code>n</code>
+ * @param start &gt;= 0; start address
+ * @param end &gt; start; end address (exclusive)
+ * @param spec non-null; register spec representing the variable
*/
- public void set(int n, Entry entry) {
- set0(n, entry);
+ public void set(int n, int start, int end, RegisterSpec spec) {
+ set0(n, new Entry(start, end, spec));
}
/**
- * Does a human-friendly dump of this instance.
+ * Sets the entry at the given index.
*
- * @param out non-null; where to dump
- * @param prefix non-null; prefix to attach to each line of output
- */
- public void debugPrint(PrintStream out, String prefix) {
- int sz = size();
-
- for (int i = 0; i < sz; i++) {
- out.print(prefix);
- out.println(get(i));
- }
- }
-
- /**
- * Disposition of a local entry.
+ * @param n &gt;= 0, &lt; size(); which index
+ * @param entry non-null; the entry to set at <code>n</code>
*/
- public static enum Disposition {
- /** local started (introduced) */
- START,
-
- /** local ended without being replaced */
- END_SIMPLY,
-
- /** local ended because it was directly replaced */
- END_REPLACED,
-
- /** local ended because it was moved to a different register */
- END_MOVED,
-
- /**
- * local ended because the previous local clobbered this one
- * (because it is category-2)
- */
- END_CLOBBERED_BY_PREV,
-
- /**
- * local ended because the next local clobbered this one
- * (because this one is a category-2)
- */
- END_CLOBBERED_BY_NEXT;
+ public void set(int n, Entry entry) {
+ set0(n, entry);
}
/**
* Entry in a local list.
*/
- public static class Entry implements Comparable<Entry> {
- /** &gt;= 0; address */
- private final int address;
+ public static class Entry {
+ /** &gt;= 0; start address */
+ private final int start;
- /** non-null; disposition of the local */
- private final Disposition disposition;
+ /** &gt; start; end address (exclusive) */
+ private final int end;
/** non-null; register spec representing the variable */
private final RegisterSpec spec;
- /** non-null; variable type (derived from {@code spec}) */
+ /** non-null; variable type */
private final CstType type;
-
+
/**
* Constructs an instance.
*
- * @param address &gt;= 0; address
- * @param disposition non-null; disposition of the local
+ * @param start &gt;= 0; start address
+ * @param end &gt; start; end address (exclusive)
* @param spec non-null; register spec representing the variable
*/
- public Entry(int address, Disposition disposition, RegisterSpec spec) {
- if (address < 0) {
- throw new IllegalArgumentException("address < 0");
+ public Entry(int start, int end, RegisterSpec spec) {
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
}
- if (disposition == null) {
- throw new NullPointerException("disposition == null");
+ if (end <= start) {
+ throw new IllegalArgumentException("end <= start");
}
try {
@@ -156,78 +287,37 @@ public final class LocalList extends FixedSizeList {
throw new NullPointerException("spec == null");
}
- this.address = address;
- this.disposition = disposition;
+ this.start = start;
+ this.end = end;
this.spec = spec;
- this.type = CstType.intern(spec.getType());
- }
- /** {@inheritDoc} */
- public String toString() {
- return Integer.toHexString(address) + " " + disposition + " " +
- spec;
- }
-
- /** {@inheritDoc} */
- public boolean equals(Object other) {
- if (!(other instanceof Entry)) {
- return false;
- }
-
- return (compareTo((Entry) other) == 0);
- }
-
- /**
- * Compares by (in priority order) address, end then start
- * disposition (variants of end are all consistered
- * equivalent), and spec.
- *
- * @param other non-null; entry to compare to
- * @return {@code -1..1}; standard result of comparison
- */
- public int compareTo(Entry other) {
- if (address < other.address) {
- return -1;
- } else if (address > other.address) {
- return 1;
- }
-
- boolean thisIsStart = isStart();
- boolean otherIsStart = other.isStart();
-
- if (thisIsStart != otherIsStart) {
- return thisIsStart ? 1 : -1;
+ if (spec.getType() == Type.KNOWN_NULL) {
+ /*
+ * KNOWN_NULL's descriptor is '<null>', which we do
+ * not want to emit. Everything else is as expected.
+ */
+ this.type = CstType.OBJECT;
+ } else {
+ this.type = CstType.intern(spec.getType());
}
-
- return spec.compareTo(other.spec);
}
/**
- * Gets the address.
+ * Gets the start address.
*
- * @return &gt;= 0; the address
+ * @return &gt;= 0; the start address
*/
- public int getAddress() {
- return address;
+ public int getStart() {
+ return start;
}
/**
- * Gets the disposition.
+ * Gets the end address (exclusive).
*
- * @return non-null; the disposition
+ * @return &gt; start; the end address (exclusive)
*/
- public Disposition getDisposition() {
- return disposition;
- }
-
- /**
- * Gets whether this is a local start. This is just shorthand for
- * {@code getDisposition() == Disposition.START}.
- *
- * @return {@code true} iff this is a start
- */
- public boolean isStart() {
- return disposition == Disposition.START;
+ public int getEnd() {
+ return end;
}
/**
@@ -282,8 +372,8 @@ public final class LocalList extends FixedSizeList {
* @return <code>true</code> iff this instance matches
* <code>spec</code>
*/
- public boolean matches(RegisterSpec otherSpec) {
- return spec.equalsUsingSimpleType(otherSpec);
+ public boolean matches(RegisterSpec spec) {
+ return spec.equals(this.spec);
}
/**
@@ -295,619 +385,18 @@ public final class LocalList extends FixedSizeList {
* <code>other</code>
*/
public boolean matches(Entry other) {
- return matches(other.spec);
+ return other.spec.equals(this.spec);
}
/**
- * Returns an instance just like this one but with the disposition
- * set as given
+ * Returns an instance just like this one, except with the end
+ * address altered to be the one given.
*
- * @param disposition non-null; the new disposition
+ * @param newEnd &gt; getStart(); the end address of the new instance
* @return non-null; an appropriately-constructed instance
*/
- public Entry withDisposition(Disposition disposition) {
- if (disposition == this.disposition) {
- return this;
- }
-
- return new Entry(address, disposition, spec);
- }
- }
-
- /**
- * Constructs an instance for the given method, based on the given
- * block order and intermediate local information.
- *
- * @param insns non-null; instructions to convert
- * @return non-null; the constructed list
- */
- public static LocalList make(DalvInsnList insns) {
- int sz = insns.size();
-
- /*
- * Go through the insn list, looking for all the local
- * variable pseudoinstructions, splitting out LocalSnapshots
- * into separate per-variable starts, adding explicit ends
- * wherever a variable is replaced or moved, and collecting
- * these and all the other local variable "activity"
- * together into an output list (without the other insns).
- *
- * Note: As of this writing, this method won't be handed any
- * insn lists that contain local ends, but I (danfuzz) expect
- * that to change at some point, when we start feeding that
- * info explicitly into the rop layer rather than only trying
- * to infer it. So, given that expectation, this code is
- * written to deal with them.
- */
-
- MakeState state = new MakeState(sz);
-
- for (int i = 0; i < sz; i++) {
- DalvInsn insn = insns.get(i);
-
- if (insn instanceof LocalSnapshot) {
- RegisterSpecSet snapshot =
- ((LocalSnapshot) insn).getLocals();
- state.snapshot(insn.getAddress(), snapshot);
- } else if (insn instanceof LocalStart) {
- RegisterSpec local = ((LocalStart) insn).getLocal();
- state.startLocal(insn.getAddress(), local);
- } else if (insn instanceof LocalEnd) {
- RegisterSpec local = ((LocalEnd) insn).getLocal();
- state.endLocal(insn.getAddress(), local);
- }
- }
-
- LocalList result = state.finish();
-
- if (DEBUG) {
- debugVerify(result);
+ public Entry withEnd(int newEnd) {
+ return new Entry(start, newEnd, spec);
}
-
- return result;
- }
-
- /**
- * Debugging helper that verifies the constraint that a list doesn't
- * contain any redundant local starts and that local ends that are
- * due to replacements are properly annotated.
- */
- private static void debugVerify(LocalList locals) {
- try {
- debugVerify0(locals);
- } catch (RuntimeException ex) {
- int sz = locals.size();
- for (int i = 0; i < sz; i++) {
- System.err.println(locals.get(i));
- }
- throw ex;
- }
-
}
-
- /**
- * Helper for {@link #debugVerify} which does most of the work.
- */
- private static void debugVerify0(LocalList locals) {
- int sz = locals.size();
- Entry[] active = new Entry[65536];
-
- for (int i = 0; i < sz; i++) {
- Entry e = locals.get(i);
- int reg = e.getRegister();
-
- if (e.isStart()) {
- Entry already = active[reg];
-
- if ((already != null) && e.matches(already)) {
- throw new RuntimeException("redundant start at " +
- Integer.toHexString(e.getAddress()) + ": got " +
- e + "; had " + already);
- }
-
- active[reg] = e;
- } else {
- if (active[reg] == null) {
- throw new RuntimeException("redundant end at " +
- Integer.toHexString(e.getAddress()));
- }
-
- int addr = e.getAddress();
- boolean foundStart = false;
-
- for (int j = i + 1; j < sz; j++) {
- Entry test = locals.get(j);
- if (test.getAddress() != addr) {
- break;
- }
- if (test.getRegisterSpec().getReg() == reg) {
- if (test.isStart()) {
- if (e.getDisposition()
- != Disposition.END_REPLACED) {
- throw new RuntimeException(
- "improperly marked end at " +
- Integer.toHexString(addr));
- }
- foundStart = true;
- } else {
- throw new RuntimeException(
- "redundant end at " +
- Integer.toHexString(addr));
- }
- }
- }
-
- if (!foundStart &&
- (e.getDisposition() == Disposition.END_REPLACED)) {
- throw new RuntimeException(
- "improper end replacement claim at " +
- Integer.toHexString(addr));
- }
-
- active[reg] = null;
- }
- }
- }
-
- /**
- * Intermediate state when constructing a local list.
- */
- public static class MakeState {
- /** non-null; result being collected */
- private final ArrayList<Entry> result;
-
- /**
- * &gt;= 0; running count of nulled result entries, to help with
- * sizing the final list
- */
- private int nullResultCount;
-
- /** null-ok; current register mappings */
- private RegisterSpecSet regs;
-
- /** null-ok; result indices where local ends are stored */
- private int[] endIndices;
-
- /** &gt;= 0; last address seen */
- private int lastAddress;
-
- /**
- * &gt;= 0; result index where the first element for the most
- * recent address is stored
- */
- private int startIndexForAddress;
-
- /**
- * Constructs an instance.
- */
- public MakeState(int initialSize) {
- result = new ArrayList<Entry>(initialSize);
- nullResultCount = 0;
- regs = null;
- endIndices = null;
- lastAddress = 0;
- startIndexForAddress = 0;
- }
-
- /**
- * Checks the address and other vitals as a prerequisite to
- * further processing.
- *
- * @param address &gt;= 0; address about to be processed
- * @param reg &gt;= 0; register number about to be processed
- */
- private void aboutToProcess(int address, int reg) {
- boolean first = (endIndices == null);
-
- if ((address == lastAddress) && !first) {
- return;
- }
-
- if (address < lastAddress) {
- throw new RuntimeException("shouldn't happen");
- }
-
- if (first || (reg >= endIndices.length)) {
- /*
- * This is the first allocation of the state set and
- * index array, or we need to grow. (The latter doesn't
- * happen much; in fact, we have only ever observed
- * it happening in test cases, never in "real" code.)
- */
- int newSz = reg + 1;
- RegisterSpecSet newRegs = new RegisterSpecSet(newSz);
- int[] newEnds = new int[newSz];
- Arrays.fill(newEnds, -1);
-
- if (!first) {
- newRegs.putAll(regs);
- System.arraycopy(endIndices, 0, newEnds, 0,
- endIndices.length);
- }
-
- regs = newRegs;
- endIndices = newEnds;
- }
- }
-
- /**
- * Sets the local state at the given address to the given snapshot.
- * The first call on this instance must be to this method, so that
- * the register state can be properly sized.
- *
- * @param address &gt;= 0; the address
- * @param specs non-null; spec set representing the locals
- */
- public void snapshot(int address, RegisterSpecSet specs) {
- int sz = specs.getMaxSize();
- aboutToProcess(address, sz - 1);
-
- for (int i = 0; i < sz; i++) {
- RegisterSpec oldSpec = regs.get(i);
- RegisterSpec newSpec = filterSpec(specs.get(i));
-
- if (oldSpec == null) {
- if (newSpec != null) {
- startLocal(address, newSpec);
- }
- } else if (newSpec == null) {
- endLocal(address, oldSpec);
- } else if (! newSpec.equalsUsingSimpleType(oldSpec)) {
- endLocal(address, oldSpec);
- startLocal(address, newSpec);
- }
- }
- }
-
- /**
- * Starts a local at the given address.
- *
- * @param address &gt;= 0; the address
- * @param startedLocal non-null; spec representing the started local
- */
- public void startLocal(int address, RegisterSpec startedLocal) {
- int regNum = startedLocal.getReg();
-
- startedLocal = filterSpec(startedLocal);
- aboutToProcess(address, regNum);
-
- RegisterSpec existingLocal = regs.get(regNum);
-
- if (startedLocal.equalsUsingSimpleType(existingLocal)) {
- // Silently ignore a redundant start.
- return;
- }
-
- RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal);
- if (movedLocal != null) {
- /*
- * The same variable was moved from one register to another.
- * So add an end for its old location.
- */
- addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal);
- }
-
- int endAt = endIndices[regNum];
-
- if (existingLocal != null) {
- /*
- * There is an existing (but non-matching) local.
- * Add an explicit end for it.
- */
- add(address, Disposition.END_REPLACED, existingLocal);
- } else if (endAt >= 0) {
- /*
- * Look for an end local for the same register at the
- * same address. If found, then update it or delete
- * it, depending on whether or not it represents the
- * same variable as the one being started.
- */
- Entry endEntry = result.get(endAt);
- if (endEntry.getAddress() == address) {
- if (endEntry.matches(startedLocal)) {
- /*
- * There was already an end local for the same
- * variable at the same address. This turns
- * out to be superfluous, as we are starting
- * up the exact same local. This situation can
- * happen when a single local variable got
- * somehow "split up" during intermediate
- * processing. In any case, rather than represent
- * the end-then-start, just remove the old end.
- */
- result.set(endAt, null);
- nullResultCount++;
- regs.put(startedLocal);
- endIndices[regNum] = -1;
- return;
- } else {
- /*
- * There was a different variable ended at the
- * same address. Update it to indicate that
- * it was ended due to a replacement (rather than
- * ending for no particular reason).
- */
- endEntry = endEntry.withDisposition(
- Disposition.END_REPLACED);
- result.set(endAt, endEntry);
- }
- }
- }
-
- /*
- * The code above didn't find and remove an unnecessary
- * local end, so we now have to add one or more entries to
- * the output to capture the transition.
- */
-
- /*
- * If the local just below (in the register set at reg-1)
- * is of category-2, then it is ended by this new start.
- */
- if (regNum > 0) {
- RegisterSpec justBelow = regs.get(regNum - 1);
- if ((justBelow != null) && justBelow.isCategory2()) {
- addOrUpdateEnd(address,
- Disposition.END_CLOBBERED_BY_NEXT,
- justBelow);
- }
- }
-
- /*
- * Similarly, if this local is category-2, then the local
- * just above (if any) is ended by the start now being
- * emitted.
- */
- if (startedLocal.isCategory2()) {
- RegisterSpec justAbove = regs.get(regNum + 1);
- if (justAbove != null) {
- addOrUpdateEnd(address,
- Disposition.END_CLOBBERED_BY_PREV,
- justAbove);
- }
- }
-
- /*
- * TODO: Add an end for the same local in a different reg,
- * if any (that is, if the local migrates from vX to vY,
- * we should note that as a local end in vX).
- */
-
- add(address, Disposition.START, startedLocal);
- }
-
- /**
- * Ends a local at the given address.
- *
- * @param address &gt;= 0; the address
- * @param endedLocal non-null; spec representing the local being ended
- */
- public void endLocal(int address, RegisterSpec endedLocal) {
- int regNum = endedLocal.getReg();
-
- endedLocal = filterSpec(endedLocal);
- aboutToProcess(address, regNum);
-
- int endAt = endIndices[regNum];
-
- if (endAt >= 0) {
- /*
- * The local in the given register is already ended.
- * Silently return without adding anything to the result.
- */
- return;
- }
-
- // Check for start and end at the same address.
- if (checkForEmptyRange(address, endedLocal)) {
- return;
- }
-
- add(address, Disposition.END_SIMPLY, endedLocal);
- }
-
- /**
- * Helper for {@link #endLocal}, which handles the cases where
- * and end local is issued at the same address as a start local
- * for the same register. If this case is found, then this
- * method will remove the start (as the local was never actually
- * active), update the {@link #endIndices} to be accurate, and
- * if needed update the newly-active end to reflect an altered
- * disposition.
- *
- * @param address &gt;= 0; the address
- * @param endedLocal non-null; spec representing the local being ended
- * @return {@code true} iff this method found the case in question
- * and adjusted things accordingly
- */
- private boolean checkForEmptyRange(int address,
- RegisterSpec endedLocal) {
- int at = result.size() - 1;
- Entry entry;
-
- // Look for a previous entry at the same address.
- for (/*at*/; at >= 0; at--) {
- entry = result.get(at);
-
- if (entry == null) {
- continue;
- }
-
- if (entry.getAddress() != address) {
- // We didn't find any match at the same address.
- return false;
- }
-
- if (entry.matches(endedLocal)) {
- break;
- }
- }
-
- /*
- * In fact, we found that the endedLocal had started at the
- * same address, so do all the requisite cleanup.
- */
-
- regs.remove(endedLocal);
- result.set(at, null);
- nullResultCount++;
-
- int regNum = endedLocal.getReg();
- boolean found = false;
- entry = null;
-
- // Now look back further to update where the register ended.
- for (at--; at >= 0; at--) {
- entry = result.get(at);
-
- if (entry == null) {
- continue;
- }
-
- if (entry.getRegisterSpec().getReg() == regNum) {
- found = true;
- break;
- }
- }
-
- if (found) {
- // We found an end for the same register.
- endIndices[regNum] = at;
-
- if (entry.getAddress() == address) {
- /*
- * It's still the same address, so update the
- * disposition.
- */
- result.set(at,
- entry.withDisposition(Disposition.END_SIMPLY));
- }
- }
-
- return true;
- }
-
- /**
- * Converts a given spec into the form acceptable for use in a
- * local list. This, in particular, transforms the "known
- * null" type into simply {@code Object}. This method needs to
- * be called for any spec that is on its way into a locals
- * list.
- *
- * <p>This isn't necessarily the cleanest way to achieve the
- * goal of not representing known nulls in a locals list, but
- * it gets the job done.</p>
- *
- * @param orig null-ok; the original spec
- * @return null-ok; an appropriately modified spec, or the
- * original if nothing needs to be done
- */
- private static RegisterSpec filterSpec(RegisterSpec orig) {
- if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) {
- return orig.withType(Type.OBJECT);
- }
-
- return orig;
- }
-
- /**
- * Adds an entry to the result, updating the adjunct tables
- * accordingly.
- *
- * @param address &gt;= 0; the address
- * @param disposition non-null; the disposition
- * @param spec non-null; spec representing the local
- */
- private void add(int address, Disposition disposition,
- RegisterSpec spec) {
- int regNum = spec.getReg();
-
- result.add(new Entry(address, disposition, spec));
-
- if (disposition == Disposition.START) {
- regs.put(spec);
- endIndices[regNum] = -1;
- } else {
- regs.remove(spec);
- endIndices[regNum] = result.size() - 1;
- }
- }
-
- /**
- * Adds or updates an end local (changing its disposition).
- *
- * @param address &gt;= 0; the address
- * @param disposition non-null; the disposition
- * @param spec non-null; spec representing the local
- */
- private void addOrUpdateEnd(int address, Disposition disposition,
- RegisterSpec spec) {
- if (disposition == Disposition.START) {
- throw new RuntimeException("shouldn't happen");
- }
-
- int regNum = spec.getReg();
- int endAt = endIndices[regNum];
-
- if (endAt >= 0) {
- Entry endEntry = result.get(endAt);
- if ((endEntry.getAddress() == address) &&
- endEntry.getRegisterSpec().equals(spec)) {
- result.set(endAt, endEntry.withDisposition(disposition));
- regs.remove(spec);
- return;
- }
- }
-
- add(address, disposition, spec);
- }
-
- /**
- * Finishes processing altogether and gets the result.
- *
- * @return non-null; the result list
- */
- public LocalList finish() {
- aboutToProcess(Integer.MAX_VALUE, 0);
-
- int resultSz = result.size();
- int finalSz = resultSz - nullResultCount;
-
- if (finalSz == 0) {
- return EMPTY;
- }
-
- /*
- * Collect an array of only the non-null entries, and then
- * sort it to get a consistent order for everything: Local
- * ends and starts for a given address could come in any
- * order, but we want ends before starts as well as
- * registers in order (within ends or starts).
- */
-
- Entry[] resultArr = new Entry[finalSz];
-
- if (resultSz == finalSz) {
- result.toArray(resultArr);
- } else {
- int at = 0;
- for (Entry e : result) {
- if (e != null) {
- resultArr[at++] = e;
- }
- }
- }
-
- Arrays.sort(resultArr);
-
- LocalList resultList = new LocalList(finalSz);
-
- for (int i = 0; i < finalSz; i++) {
- resultList.set(i, resultArr[i]);
- }
-
- resultList.setImmutable();
- return resultList;
- }
- }
}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
index 3ffd27696..519452b64 100644
--- a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -16,8 +16,6 @@
package com.android.dx.dex.file;
-import com.android.dx.dex.code.DalvCode;
-import com.android.dx.dex.code.DalvInsnList;
import com.android.dx.dex.code.LocalList;
import com.android.dx.dex.code.PositionList;
import com.android.dx.rop.cst.CstMethodRef;
@@ -43,28 +41,20 @@ import static com.android.dx.dex.file.DebugInfoConstants.*;
public class DebugInfoDecoder {
/** encoded debug info */
private final byte[] encoded;
-
/** positions decoded */
private final ArrayList<PositionEntry> positions;
-
/** locals decoded */
private final ArrayList<LocalEntry> locals;
-
/** size of code block in code units */
private final int codesize;
-
/** indexed by register, the last local variable live in a reg */
private final LocalEntry[] lastEntryForReg;
-
/** method descriptor of method this debug info is for */
private final Prototype desc;
-
/** true if method is static */
private final boolean isStatic;
-
/** dex file this debug info will be stored in */
private final DexFile file;
-
/**
* register size, in register units, of the register space
* used by this method
@@ -91,11 +81,8 @@ public class DebugInfoDecoder {
* @param ref method descriptor of method this debug info is for
* @param file dex file this debug info will be stored in
*/
- DebugInfoDecoder(byte[] encoded, int codesize, int regSize,
+ DebugInfoDecoder (byte[] encoded, int codesize, int regSize,
boolean isStatic, CstMethodRef ref, DexFile file) {
- if (encoded == null) {
- throw new NullPointerException("encoded == null");
- }
this.encoded = encoded;
this.isStatic = isStatic;
@@ -103,37 +90,24 @@ public class DebugInfoDecoder {
this.file = file;
this.regSize = regSize;
- positions = new ArrayList<PositionEntry>();
- locals = new ArrayList<LocalEntry>();
+ positions = new ArrayList();
+ locals = new ArrayList();
this.codesize = codesize;
lastEntryForReg = new LocalEntry[regSize];
- int idx = -1;
-
- try {
- idx = file.getStringIds().indexOf(new CstUtf8("this"));
- } catch (IllegalArgumentException ex) {
- /*
- * Silently tolerate not finding "this". It just means that
- * no method has local variable info that looks like
- * a standard instance method.
- */
- }
-
- thisStringIdx = idx;
+ thisStringIdx = file.getStringIds().indexOf(new CstUtf8("this"));
}
/**
* An entry in the resulting postions table
*/
- static private class PositionEntry {
+ static class PositionEntry {
/** bytecode address */
- public int address;
-
+ int address;
/** line number */
- public int line;
+ int line;
- public PositionEntry(int address, int line) {
+ PositionEntry(int address, int line) {
this.address = address;
this.line = line;
}
@@ -142,40 +116,37 @@ public class DebugInfoDecoder {
/**
* An entry in the resulting locals table
*/
- static private class LocalEntry {
- /** address of event */
- public int address;
+ static class LocalEntry {
+ LocalEntry(int start, int reg, int nameIndex, int typeIndex,
+ int signatureIndex) {
+ this.start = start;
+ this.reg = reg;
+ this.nameIndex = nameIndex;
+ this.typeIndex = typeIndex;
+ this.signatureIndex = signatureIndex;
+ }
- /** {@code true} iff it's a local start */
- public boolean isStart;
+ /** start of address range */
+ int start;
+
+ /**
+ * End of address range. Initialized to MAX_VALUE here but will
+ * be set to no more than 1 + max bytecode address of method.
+ */
+ int end = Integer.MAX_VALUE;
/** register number */
- public int reg;
+ int reg;
/** index of name in strings table */
- public int nameIndex;
+ int nameIndex;
/** index of type in types table */
- public int typeIndex;
+ int typeIndex;
/** index of type signature in strings table */
- public int signatureIndex;
+ int signatureIndex;
- public LocalEntry(int address, boolean isStart, int reg, int nameIndex,
- int typeIndex, int signatureIndex) {
- this.address = address;
- this.isStart = isStart;
- this.reg = reg;
- this.nameIndex = nameIndex;
- this.typeIndex = typeIndex;
- this.signatureIndex = signatureIndex;
- }
-
- public String toString() {
- return String.format("[%x %s v%d %04x %04x %04x]",
- address, isStart ? "start" : "end", reg,
- nameIndex, typeIndex, signatureIndex);
- }
}
/**
@@ -195,6 +166,13 @@ public class DebugInfoDecoder {
* @return locals list in ascending address order.
*/
public List<LocalEntry> getLocals() {
+ // TODO move this loop:
+ // Any variable that didnt end ends now
+ for (LocalEntry local: locals) {
+ if (local.end == Integer.MAX_VALUE) {
+ local.end = codesize;
+ }
+ }
return locals;
}
@@ -251,8 +229,8 @@ public class DebugInfoDecoder {
if (!isStatic) {
// Start off with implicit 'this' entry
- LocalEntry thisEntry =
- new LocalEntry(0, true, curReg, thisStringIdx, 0, 0);
+ LocalEntry thisEntry
+ = new LocalEntry(0, curReg, thisStringIdx, 0, 0);
locals.add(thisEntry);
lastEntryForReg[curReg] = thisEntry;
curReg++;
@@ -264,19 +242,15 @@ public class DebugInfoDecoder {
int nameIdx = readStringIndex(bs);
- if (nameIdx == -1) {
- /*
- * Unnamed parameter; often but not always filled in by an
- * extended start op after the prologue
- */
- le = new LocalEntry(0, true, curReg, -1, 0, 0);
+ if(nameIdx == -1) {
+ // unnamed parameter
} else {
- // TODO: Final 0 should be idx of paramType.getDescriptor().
- le = new LocalEntry(0, true, curReg, nameIdx, 0, 0);
+ // final '0' should be idx of paramType.getDescriptor()
+ le = new LocalEntry(0, curReg, nameIdx, 0, 0);
+ locals.add(le);
+ lastEntryForReg[curReg] = le;
}
- locals.add(le);
- lastEntryForReg[curReg] = le;
curReg += paramType.getCategory();
}
@@ -295,7 +269,15 @@ public class DebugInfoDecoder {
int nameIdx = readStringIndex(bs);
int typeIdx = readStringIndex(bs);
LocalEntry le = new LocalEntry(
- address, true, reg, nameIdx, typeIdx, 0);
+ address, reg, nameIdx, typeIdx, 0);
+
+ // a "start" is implicitly the "end" of whatever was
+ // previously defined in the register
+ if (lastEntryForReg[reg] != null
+ && lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+
+ lastEntryForReg[reg].end = address;
+ }
locals.add(le);
lastEntryForReg[reg] = le;
@@ -308,7 +290,21 @@ public class DebugInfoDecoder {
int typeIdx = readStringIndex(bs);
int sigIdx = readStringIndex(bs);
LocalEntry le = new LocalEntry(
- address, true, reg, nameIdx, typeIdx, sigIdx);
+ address, reg, nameIdx, typeIdx, sigIdx);
+
+ // a "start" is implicitly the "end" of whatever was
+ // previously defined in the register
+ if (lastEntryForReg[reg] != null
+ && lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+
+ lastEntryForReg[reg].end = address;
+
+ // A 0-length entry. Almost certainly a "this"
+ // with a signature.
+ if (lastEntryForReg[reg].start == address) {
+ locals.remove(lastEntryForReg[reg]);
+ }
+ }
locals.add(le);
lastEntryForReg[reg] = le;
@@ -323,17 +319,16 @@ public class DebugInfoDecoder {
try {
prevle = lastEntryForReg[reg];
- if (prevle.isStart) {
- throw new RuntimeException("nonsensical "
- + "RESTART_LOCAL on live register v"
- + reg);
+ if (lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+ throw new RuntimeException ("nonsensical "
+ + "RESTART_LOCAL on live register v"+reg);
}
-
- le = new LocalEntry(address, true, reg,
+ le = new LocalEntry(address, reg,
prevle.nameIndex, prevle.typeIndex, 0);
+
} catch (NullPointerException ex) {
- throw new RuntimeException(
- "Encountered RESTART_LOCAL on new v" + reg);
+ throw new RuntimeException
+ ("Encountered RESTART_LOCAL on new v" +reg);
}
locals.add(le);
@@ -343,27 +338,20 @@ public class DebugInfoDecoder {
case DBG_END_LOCAL: {
int reg = readUnsignedLeb128(bs);
- LocalEntry prevle;
- LocalEntry le;
-
- try {
- prevle = lastEntryForReg[reg];
-
- if (!prevle.isStart) {
- throw new RuntimeException("nonsensical "
- + "END_LOCAL on dead register v" + reg);
+ boolean found = false;
+ for (int i = locals.size() - 1; i >= 0; i--) {
+ if (locals.get(i).reg == reg) {
+ locals.get(i).end = address;
+ found = true;
+ break;
}
-
- le = new LocalEntry(address, false, reg,
- prevle.nameIndex, prevle.typeIndex,
- prevle.signatureIndex);
- } catch (NullPointerException ex) {
- throw new RuntimeException(
- "Encountered END_LOCAL on new v" + reg);
}
- locals.add(le);
- lastEntryForReg[reg] = le;
+ if (!found) {
+ throw new RuntimeException(
+ "Encountered LOCAL_END without local start: v"
+ + reg);
+ }
}
break;
@@ -415,48 +403,40 @@ public class DebugInfoDecoder {
* throwing an exception if they do not match. Used to validate the
* encoder.
*
- * @param info encoded debug info
- * @param file non-null; file to refer to during decoding
- * @param ref non-null; method whose info is being decoded
- * @param code non-null; original code object that was encoded
- * @param isStatic whether the method is static
+ * @param linecodes encoded debug info
+ * @param codeSize size of insn block in code units
+ * @param countRegisters size of used register block in register units
+ * @param pl position list to verify against
+ * @param ll locals list to verify against.
*/
- public static void validateEncode(byte[] info, DexFile file,
- CstMethodRef ref, DalvCode code, boolean isStatic) {
- PositionList pl = code.getPositions();
- LocalList ll = code.getLocals();
- DalvInsnList insns = code.getInsns();
- int codeSize = insns.codeSize();
- int countRegisters = insns.getRegistersSize();
-
+ public static void validateEncode(byte[] linecodes, int codeSize,
+ int countRegisters, PositionList pl, LocalList ll,
+ boolean isStatic, CstMethodRef ref, DexFile file) {
+
try {
- validateEncode0(info, codeSize, countRegisters,
+ validateEncode0(linecodes, codeSize, countRegisters,
isStatic, ref, file, pl, ll);
} catch (RuntimeException ex) {
- System.err.println("instructions:");
- insns.debugPrint(System.err, " ", true);
- System.err.println("local list:");
- ll.debugPrint(System.err, " ");
+// System.err.println(ex.toString()
+// + " while processing " + ref.toHuman());
throw ExceptionWithContext.withContext(ex,
"while processing " + ref.toHuman());
}
}
+
- private static void validateEncode0(byte[] info, int codeSize,
+ private static void validateEncode0(byte[] linecodes, int codeSize,
int countRegisters, boolean isStatic, CstMethodRef ref,
DexFile file, PositionList pl, LocalList ll) {
DebugInfoDecoder decoder
- = new DebugInfoDecoder(info, codeSize, countRegisters,
+ = new DebugInfoDecoder(linecodes, codeSize, countRegisters,
isStatic, ref, file);
decoder.decode();
- /*
- * Go through the decoded position entries, matching up
- * with original entries.
- */
+ List<PositionEntry> decodedEntries;
- List<PositionEntry> decodedEntries = decoder.getPositionList();
+ decodedEntries = decoder.getPositionList();
if (decodedEntries.size() != pl.size()) {
throw new RuntimeException(
@@ -464,7 +444,7 @@ public class DebugInfoDecoder {
+ decodedEntries.size() + " expected " + pl.size());
}
- for (PositionEntry entry : decodedEntries) {
+ for (PositionEntry entry: decodedEntries) {
boolean found = false;
for (int i = pl.size() - 1; i >= 0; i--) {
PositionList.Entry ple = pl.get(i);
@@ -482,111 +462,41 @@ public class DebugInfoDecoder {
}
}
- /*
- * Go through the original local list, in order, matching up
- * with decoded entries.
- */
+ List<LocalEntry> decodedLocals;
+
+ decodedLocals = decoder.getLocals();
- List<LocalEntry> decodedLocals = decoder.getLocals();
- int thisStringIdx = decoder.thisStringIdx;
- int decodedSz = decodedLocals.size();
int paramBase = decoder.getParamBase();
- /*
- * Preflight to fill in any parameters that were skipped in
- * the prologue (including an implied "this") but then
- * identified by full signature.
- */
- for (int i = 0; i < decodedSz; i++) {
- LocalEntry entry = decodedLocals.get(i);
- int idx = entry.nameIndex;
-
- if ((idx < 0) || (idx == thisStringIdx)) {
- for (int j = i + 1; j < decodedSz; j++) {
- LocalEntry e2 = decodedLocals.get(j);
- if (e2.address != 0) {
- break;
- }
- if ((entry.reg == e2.reg) && e2.isStart) {
- decodedLocals.set(i, e2);
- decodedLocals.remove(j);
- decodedSz--;
- break;
- }
- }
- }
- }
-
- int origSz = ll.size();
- int decodeAt = 0;
- boolean problem = false;
+ int matchedLocalsEntries = 0;
- for (int i = 0; i < origSz; i++) {
- LocalList.Entry origEntry = ll.get(i);
+ for (LocalEntry entry: decodedLocals) {
+ boolean found = false;
+ for (int i = ll.size() - 1; i >= 0; i--) {
+ LocalList.Entry le = ll.get(i);
- if (origEntry.getDisposition()
- == LocalList.Disposition.END_REPLACED) {
/*
- * The encoded list doesn't represent replacements, so
- * ignore them for the sake of comparison.
+ * If an entry is a method parameter, then the original
+ * entry may not be marked as starting at 0. However, the
+ * end address should still match.
*/
- continue;
- }
-
- LocalEntry decodedEntry;
-
- do {
- decodedEntry = decodedLocals.get(decodeAt);
- if (decodedEntry.nameIndex >= 0) {
+ if ((entry.start == le.getStart()
+ || (entry.start == 0 && entry.reg >= paramBase))
+ && entry.end == le.getEnd()
+ && entry.reg == le.getRegister()) {
+ found = true;
+ matchedLocalsEntries++;
break;
}
- /*
- * A negative name index means this is an anonymous
- * parameter, and we shouldn't expect to see it in the
- * original list. So, skip it.
- */
- decodeAt++;
- } while (decodeAt < decodedSz);
-
- int decodedAddress = decodedEntry.address;
-
- if (decodedEntry.reg != origEntry.getRegister()) {
- System.err.println("local register mismatch at orig " + i +
- " / decoded " + decodeAt);
- problem = true;
- break;
- }
-
- if (decodedEntry.isStart != origEntry.isStart()) {
- System.err.println("local start/end mismatch at orig " + i +
- " / decoded " + decodeAt);
- problem = true;
- break;
}
- /*
- * The secondary check here accounts for the fact that a
- * parameter might not be marked as starting at 0 in the
- * original list.
- */
- if ((decodedAddress != origEntry.getAddress())
- && !((decodedAddress == 0)
- && (decodedEntry.reg >= paramBase))) {
- System.err.println("local address mismatch at orig " + i +
- " / decoded " + decodeAt);
- problem = true;
- break;
+ if (!found) {
+ throw new RuntimeException("Could not match local entry");
}
-
- decodeAt++;
}
- if (problem) {
- System.err.println("decoded locals:");
- for (LocalEntry e : decodedLocals) {
- System.err.println(" " + e);
- }
- throw new RuntimeException("local table problem");
+ if (matchedLocalsEntries != ll.size()) {
+ throw new RuntimeException("Locals tables did not match");
}
}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
index 780e18d5b..49781bdd8 100644
--- a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
+++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
@@ -56,10 +56,10 @@ public final class DebugInfoEncoder {
private static final boolean DEBUG = false;
/** null-ok; positions (line numbers) to encode */
- private final PositionList positions;
+ private final PositionList positionlist;
/** null-ok; local variables to encode */
- private final LocalList locals;
+ private final LocalList locallist;
private final ByteArrayAnnotatedOutput output;
private final DexFile file;
@@ -108,8 +108,8 @@ public final class DebugInfoEncoder {
public DebugInfoEncoder(PositionList pl, LocalList ll,
DexFile file, int codeSize, int regSize,
boolean isStatic, CstMethodRef ref) {
- this.positions = pl;
- this.locals = ll;
+ this.positionlist = pl;
+ this.locallist = ll;
this.file = file;
output = new ByteArrayAnnotatedOutput();
this.desc = ref.getPrototype();
@@ -193,11 +193,18 @@ public final class DebugInfoEncoder {
private byte[] convert0() throws IOException {
ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
- ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
+ ArrayList<LocalList.Entry> sortedLocalsStart = buildLocalsStart();
+
+ // Parameter locals are removed from sortedLocalsStart here.
+ ArrayList<LocalList.Entry> methodArgs
+ = extractMethodArguments(sortedLocalsStart);
+
+ ArrayList<LocalList.Entry> sortedLocalsEnd
+ = buildLocalsEnd(sortedLocalsStart);
emitHeader(sortedPositions, methodArgs);
- // TODO: Make this mark be the actual prologue end.
+ // TODO: Make this mark the actual prologue end.
output.writeByte(DBG_SET_PROLOGUE_END);
if (annotateTo != null || debugPrint != null) {
@@ -205,37 +212,56 @@ public final class DebugInfoEncoder {
}
int szp = sortedPositions.size();
- int szl = locals.size();
+ int szl = sortedLocalsStart.size();
// Current index in sortedPositions
int curp = 0;
- // Current index in locals
- int curl = 0;
+ // Current index in sortedLocalsStart
+ int curls = 0;
+ // Current index in sortedLocalsEnd
+ int curle = 0;
for (;;) {
/*
* Emit any information for the current address.
*/
- curl = emitLocalsAtAddress(curl);
+ curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls,
+ sortedLocalsStart);
+
+ /*
+ * Our locals-sorted-by-range-end has reached the end
+ * of the code block. Ignore everything else.
+ */
+ if (address == codeSize) {
+ curle = szl;
+ }
+
+ curls = emitLocalStartsAtAddress(curls, sortedLocalsStart);
+
curp = emitPositionsAtAddress(curp, sortedPositions);
/*
* Figure out what the next important address is.
*/
- int nextAddrL = Integer.MAX_VALUE; // local variable
- int nextAddrP = Integer.MAX_VALUE; // position (line number)
+ int nextAddrLS = Integer.MAX_VALUE; // local start
+ int nextAddrLE = Integer.MAX_VALUE; // local end
+ int nextAddrP = Integer.MAX_VALUE; // position (line number)
+
+ if (curls < szl) {
+ nextAddrLS = sortedLocalsStart.get(curls).getStart();
+ }
- if (curl < szl) {
- nextAddrL = locals.get(curl).getAddress();
+ if (curle < szl) {
+ nextAddrLE = sortedLocalsEnd.get(curle).getEnd();
}
if (curp < szp) {
nextAddrP = sortedPositions.get(curp).getAddress();
}
- int next = Math.min(nextAddrP, nextAddrL);
+ int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE));
// No next important address == done.
if (next == Integer.MAX_VALUE) {
@@ -247,7 +273,7 @@ public final class DebugInfoEncoder {
* block, stop here. Those are implied anyway.
*/
if (next == codeSize
- && nextAddrL == Integer.MAX_VALUE
+ && nextAddrLS == Integer.MAX_VALUE
&& nextAddrP == Integer.MAX_VALUE) {
break;
}
@@ -266,24 +292,76 @@ public final class DebugInfoEncoder {
}
/**
- * Emits all local variable activity that occurs at the current
- * {@link #address} starting at the given index into {@code
- * locals} and including all subsequent activity at the same
- * address.
+ * Emits all local ends that occur at the current <code>address</code>
*
- * @param curl Current index in locals
- * @return new value for <code>curl</code>
+ * @param curle Current index in sortedLocalsEnd
+ * @param sortedLocalsEnd Locals, sorted by ascending end address
+ * @param curls Current index in sortedLocalsStart
+ * @param sortedLocalsStart Locals, sorted by ascending start address
+ * @return new value for <code>curle</code>
* @throws IOException
*/
- private int emitLocalsAtAddress(int curl)
+ private int emitLocalEndsAtAddress(int curle,
+ ArrayList<LocalList.Entry> sortedLocalsEnd, int curls,
+ ArrayList<LocalList.Entry> sortedLocalsStart)
throws IOException {
- int sz = locals.size();
- // TODO: Don't emit ends implied by starts.
+ int szl = sortedLocalsEnd.size();
- while ((curl < sz)
- && (locals.get(curl).getAddress() == address)) {
- LocalList.Entry lle = locals.get(curl++);
+ // Ignore "local ends" at end of code.
+ while (curle < szl
+ && sortedLocalsEnd.get(curle).getEnd() == address
+ && address != codeSize) {
+
+ boolean skipLocalEnd = false;
+
+ /*
+ * Check to see if there's a range-start that appears at
+ * the same address for the same register. If so, the
+ * end-range is implicit so skip it.
+ */
+ for (int j = curls; j < szl
+ && sortedLocalsStart.get(j).getStart() == address
+ ; j++) {
+
+ if (sortedLocalsStart.get(j).getRegister()
+ == sortedLocalsEnd.get(curle).getRegister()) {
+ skipLocalEnd = true;
+
+ if (DEBUG) {
+ System.err.printf("skip local end v%d\n",
+ sortedLocalsEnd.get(curle).getRegister());
+ }
+ break;
+ }
+ }
+
+ if (!skipLocalEnd) {
+ emitLocalEnd(sortedLocalsEnd.get(curle));
+ }
+
+ curle++;
+ }
+ return curle;
+ }
+
+ /**
+ * Emits all local starts that occur at the current <code>address</code>
+ *
+ * @param curls Current index in sortedLocalsStart
+ * @param sortedLocalsStart Locals, sorted by ascending start address
+ * @return new value for <code>curls</code>
+ * @throws IOException
+ */
+ private int emitLocalStartsAtAddress(int curls,
+ ArrayList<LocalList.Entry> sortedLocalsStart)
+ throws IOException {
+
+ int szl = sortedLocalsStart.size();
+
+ while (curls < szl
+ && sortedLocalsStart.get(curls).getStart() == address) {
+ LocalList.Entry lle = sortedLocalsStart.get(curls++);
int reg = lle.getRegister();
LocalList.Entry prevlle = lastEntryForReg[reg];
@@ -296,45 +374,26 @@ public final class DebugInfoEncoder {
continue;
}
- // At this point we have a new entry one way or another.
+ // At this point we have a new live entry one way or another.
lastEntryForReg[reg] = lle;
- if (lle.isStart()) {
- if ((prevlle != null) && lle.matches(prevlle)) {
+ if ((prevlle != null) && lle.matches(prevlle)) {
+ if (prevlle.getEnd() == lle.getStart()) {
/*
- * The previous local in this register has the same
- * name and type as the one being introduced now, so
- * use the more efficient "restart" form.
+ * There is nothing more to do in this case: It's
+ * an adjacent range with the same register. The
+ * previous emitLocalEndsAtAddress() call skipped
+ * this local end, so we'll skip this local start
+ * as well.
*/
- if (prevlle.isStart()) {
- /*
- * We should never be handed a start when a
- * a matching local is already active.
- */
- throw new RuntimeException("shouldn't happen");
- }
- emitLocalRestart(lle);
} else {
- emitLocalStart(lle);
+ emitLocalRestart(lle);
}
} else {
- /*
- * Only emit a local end if it is *not* due to a direct
- * replacement. Direct replacements imply an end of the
- * previous local in the same register.
- *
- * TODO: Make sure the runtime can deal with implied
- * local ends from category-2 interactions, and when so,
- * also stop emitting local ends for those cases.
- */
- if (lle.getDisposition()
- != LocalList.Disposition.END_REPLACED) {
- emitLocalEnd(lle);
- }
+ emitLocalStart(lle);
}
}
-
- return curl;
+ return curls;
}
/**
@@ -465,7 +524,7 @@ public final class DebugInfoEncoder {
* a LOCAL_RESTART_EXTENDED
*/
- for (LocalList.Entry arg : lastEntryForReg) {
+ for (LocalList.Entry arg: lastEntryForReg) {
if (arg == null) {
continue;
}
@@ -484,11 +543,11 @@ public final class DebugInfoEncoder {
* @return A sorted positions list
*/
private ArrayList<PositionList.Entry> buildSortedPositions() {
- int sz = (positions == null) ? 0 : positions.size();
+ int sz = (positionlist == null) ? 0 : positionlist.size();
ArrayList<PositionList.Entry> result = new ArrayList(sz);
for (int i = 0; i < sz; i++) {
- result.add(positions.get(i));
+ result.add(positionlist.get(i));
}
// Sort ascending by address.
@@ -505,6 +564,58 @@ public final class DebugInfoEncoder {
}
/**
+ * Builds a list of locals entries sorted by ascending start address.
+ *
+ * @return A sorted locals list list
+ */
+ private ArrayList<LocalList.Entry> buildLocalsStart() {
+ int sz = (locallist == null) ? 0 : locallist.size();
+ ArrayList<LocalList.Entry> result = new ArrayList(sz);
+
+ // Add all the entries
+ for (int i = 0; i < sz; i++) {
+ LocalList.Entry e = locallist.get(i);
+ result.add(locallist.get(i));
+ }
+
+ // Sort ascending by start address.
+ Collections.sort (result, new Comparator<LocalList.Entry>() {
+ public int compare (LocalList.Entry a, LocalList.Entry b) {
+ return a.getStart() - b.getStart();
+ }
+
+ public boolean equals (Object obj) {
+ return obj == this;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Builds a list of locals entries sorted by ascending end address.
+ *
+ * @param list locals list in any order
+ * @return a sorted locals list
+ */
+ private ArrayList<LocalList.Entry> buildLocalsEnd(
+ ArrayList<LocalList.Entry> list) {
+
+ ArrayList<LocalList.Entry> sortedLocalsEnd = new ArrayList(list);
+
+ // Sort ascending by end address.
+ Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() {
+ public int compare (LocalList.Entry a, LocalList.Entry b) {
+ return a.getEnd() - b.getEnd();
+ }
+
+ public boolean equals (Object obj) {
+ return obj == this;
+ }
+ });
+ return sortedLocalsEnd;
+ }
+
+ /**
* Gets the register that begins the method's parameter range (including
* the 'this' parameter for non-static methods). The range continues until
* <code>regSize</code>
@@ -521,18 +632,24 @@ public final class DebugInfoEncoder {
* from the input list and sorted by ascending register in the
* returned list.
*
+ * @param sortedLocals locals list, sorted by ascending start address,
+ * to process; left unmodified
* @return list of non-<code>this</code> method argument locals,
* sorted by ascending register
*/
- private ArrayList<LocalList.Entry> extractMethodArguments() {
+ private ArrayList<LocalList.Entry> extractMethodArguments (
+ ArrayList<LocalList.Entry> sortedLocals) {
+
ArrayList<LocalList.Entry> result
= new ArrayList(desc.getParameterTypes().size());
+
int argBase = getParamBase();
+
BitSet seen = new BitSet(regSize - argBase);
- int sz = locals.size();
+ int sz = sortedLocals.size();
for (int i = 0; i < sz; i++) {
- LocalList.Entry e = locals.get(i);
+ LocalList.Entry e = sortedLocals.get(i);
int reg = e.getRegister();
if (reg < argBase) {
@@ -549,12 +666,12 @@ public final class DebugInfoEncoder {
}
// Sort by ascending register.
- Collections.sort(result, new Comparator<LocalList.Entry>() {
- public int compare(LocalList.Entry a, LocalList.Entry b) {
+ Collections.sort (result, new Comparator<LocalList.Entry>() {
+ public int compare (LocalList.Entry a, LocalList.Entry b) {
return a.getRegister() - b.getRegister();
}
- public boolean equals(Object obj) {
+ public boolean equals (Object obj) {
return obj == this;
}
});
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
index 0e4329b14..b0745935d 100644
--- a/dx/src/com/android/dx/dex/file/DebugInfoItem.java
+++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
@@ -141,37 +141,6 @@ public class DebugInfoItem extends OffsettedItem {
*/
private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
AnnotatedOutput out, boolean consume) {
- byte[] result = encode0(file, prefix, debugPrint, out, consume);
-
- if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
- try {
- DebugInfoDecoder.validateEncode(result, file, ref, code,
- isStatic);
- } catch (RuntimeException ex) {
- // Reconvert, annotating to System.err.
- encode0(file, "", new PrintWriter(System.err, true), null,
- false);
- throw ex;
- }
- }
-
- return result;
- }
-
- /**
- * Helper for {@link #encode} to do most of the work.
- *
- * @param file null-ok; file to refer to during encoding
- * @param prefix null-ok; prefix to attach to each line of output
- * @param debugPrint null-ok; if specified, an alternate output for
- * annotations
- * @param out null-ok; if specified, where annotations should go
- * @param consume whether to claim to have consumed output for
- * <code>out</code>
- * @return non-null; the encoded array
- */
- private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint,
- AnnotatedOutput out, boolean consume) {
PositionList positions = code.getPositions();
LocalList locals = code.getLocals();
DalvInsnList insns = code.getInsns();
@@ -187,8 +156,13 @@ public class DebugInfoItem extends OffsettedItem {
if ((debugPrint == null) && (out == null)) {
result = encoder.convert();
} else {
- result = encoder.convertAndAnnotate(prefix, debugPrint, out,
- consume);
+ result = encoder.convertAndAnnotate(
+ prefix, debugPrint, out, consume);
+ }
+
+ if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
+ DebugInfoDecoder.validateEncode(encoded, codeSize,
+ regSize, positions, locals, isStatic, ref, file);
}
return result;
diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java
index bac6ce2a4..b1e1a4be6 100644
--- a/dx/src/com/android/dx/rop/code/LocalItem.java
+++ b/dx/src/com/android/dx/rop/code/LocalItem.java
@@ -22,6 +22,7 @@ import com.android.dx.rop.cst.CstUtf8;
* A local variable item: either a name or a signature or both.
*/
public class LocalItem implements Comparable<LocalItem> {
+
/** null-ok; local variable name */
private final CstUtf8 name;
@@ -37,7 +38,7 @@ public class LocalItem implements Comparable<LocalItem> {
* @param signature null-ok; local variable signature
* @return non-null; appropriate instance.
*/
- public static LocalItem make(CstUtf8 name, CstUtf8 signature) {
+ public static LocalItem make (CstUtf8 name, CstUtf8 signature) {
if (name == null && signature == null) {
return null;
}
@@ -51,14 +52,14 @@ public class LocalItem implements Comparable<LocalItem> {
* @param name null-ok; local variable name
* @param signature null-ok; local variable signature
*/
- private LocalItem(CstUtf8 name, CstUtf8 signature) {
+ private LocalItem (CstUtf8 name, CstUtf8 signature) {
this.name = name;
this.signature = signature;
}
/** {@inheritDoc} */
@Override
- public boolean equals(Object other) {
+ public boolean equals (Object other) {
if (!(other instanceof LocalItem)) {
return false;
}
@@ -88,7 +89,7 @@ public class LocalItem implements Comparable<LocalItem> {
}
/** {@inheritDoc} */
- public int compareTo(LocalItem local) {
+ public int compareTo (LocalItem local) {
int ret;
ret = compareHandlesNulls(name, local.name);
@@ -105,7 +106,7 @@ public class LocalItem implements Comparable<LocalItem> {
/** {@inheritDoc} */
@Override
- public int hashCode() {
+ public int hashCode () {
return (name == null ? 0 : name.hashCode()) * 31
+ (signature == null ? 0 : signature.hashCode());
}
@@ -120,7 +121,7 @@ public class LocalItem implements Comparable<LocalItem> {
}
return "[" + (name == null ? "" : name.toQuoted())
- + "|" + (signature == null ? "" : signature.toQuoted());
+ + "|" + (signature == null ? "" : signature.toQuoted());
}
/**
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java
index 73af91fbc..09f7f1829 100644
--- a/dx/src/com/android/dx/rop/code/RegisterSpec.java
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -29,7 +29,7 @@ import java.util.HashMap;
* destinations of register-based operations.
*/
public final class RegisterSpec
- implements TypeBearer, ToHuman, Comparable<RegisterSpec> {
+ implements TypeBearer, ToHuman {
/** non-null; string to prefix register numbers with */
public static final String PREFIX = "v";
@@ -97,8 +97,7 @@ public final class RegisterSpec
* @param local non-null; the associated local variable
* @return non-null; an appropriately-constructed instance
*/
- public static RegisterSpec make(int reg, TypeBearer type,
- LocalItem local) {
+ public static RegisterSpec make(int reg, TypeBearer type, LocalItem local) {
if (local == null) {
throw new NullPointerException("local == null");
}
@@ -173,43 +172,6 @@ public final class RegisterSpec
}
/**
- * Like {@code equals}, but only consider the simple types of the
- * registers. That is, this compares {@code getType()} on the types
- * to ignore whatever arbitrary extra stuff might be carried around
- * by an outer {@link TypeBearer}.
- *
- * @param other null-ok; spec to compare to
- * @return {@code true} iff {@code this} and {@code other} are equal
- * in the stated way
- */
- public boolean equalsUsingSimpleType(RegisterSpec other) {
- if (!matchesVariable(other)) {
- return false;
- }
-
- return (reg == other.reg);
- }
-
- /**
- * Like {@link #equalsUsingSimpleType} but ignoring the register number.
- * This is useful to determine if two instances refer to the "same"
- * local variable.
- *
- * @param other null-ok; spec to compare to
- * @return {@code true} iff {@code this} and {@code other} are equal
- * in the stated way
- */
- public boolean matchesVariable(RegisterSpec other) {
- if (other == null) {
- return false;
- }
-
- return type.getType().equals(other.type.getType())
- && ((local == other.local)
- || ((local != null) && local.equals(other.local)));
- }
-
- /**
* Helper for {@link #equals} and {@link #ForComparison.equals},
* which actually does the test.
*
@@ -226,35 +188,6 @@ public final class RegisterSpec
|| ((this.local != null) && this.local.equals(local)));
}
- /**
- * Compares by (in priority order) register number, unwrapped type
- * (that is types not {@link TypeBearer}s, and local info.
- *
- * @param other non-null; spec to compare to
- * @return {@code -1..1}; standard result of comparison
- */
- public int compareTo(RegisterSpec other) {
- if (this.reg < other.reg) {
- return -1;
- } else if (this.reg > other.reg) {
- return 1;
- }
-
- int compare = type.getType().compareTo(other.type.getType());
-
- if (compare != 0) {
- return compare;
- }
-
- if (this.local == null) {
- return (other.local == null) ? 0 : -1;
- } else if (other.local == null) {
- return 1;
- }
-
- return this.local.compareTo(other.local);
- }
-
/** {@inheritDoc} */
@Override
public int hashCode() {
@@ -359,8 +292,6 @@ public final class RegisterSpec
* Gets the category of this instance's type. This is just a convenient
* shorthand for <code>getType().getCategory()</code>.
*
- * @see #isCategory1
- * @see #isCategory2
* @return 1..2; the category of this instance's type
*/
public int getCategory() {
@@ -368,30 +299,6 @@ public final class RegisterSpec
}
/**
- * Gets whether this instance's type is category 1. This is just a
- * convenient shorthand for <code>getType().isCategory1()</code>.
- *
- * @see #getCategory
- * @see #isCategory2
- * @return whether or not this instance's type is of category 1
- */
- public boolean isCategory1() {
- return type.getType().isCategory1();
- }
-
- /**
- * Gets whether this instance's type is category 2. This is just a
- * convenient shorthand for <code>getType().isCategory2()</code>.
- *
- * @see #getCategory
- * @see #isCategory1
- * @return whether or not this instance's type is of category 2
- */
- public boolean isCategory2() {
- return type.getType().isCategory2();
- }
-
- /**
* Gets the string form for just the register number of this instance.
*
* @return non-null; the register string form
@@ -416,16 +323,16 @@ public final class RegisterSpec
* are <code>equals()</code>, then the intersection's type bearer
* is the one from this instance. Otherwise, the intersection's
* type bearer is the <code>getType()</code> of this instance.</li>
- * <li>If the locals are <code>equals()</code>, then the local info
- * of the intersection is the local info of this instance. Otherwise,
- * the local info of the intersection is <code>null</code>.</li>
+ * <li>If the locals are <code>equals()</code>, then the local info of the
+ * intersection is the local info of this instance. Otherwise, the local info
+ * of the intersection is <code>null</code>.</li>
* </ul>
*
* @param other null-ok; instance to intersect with (or <code>null</code>)
- * @param localPrimary whether local variables are primary to the
- * intersection; if <code>true</code>, then the only non-null
- * results occur when registers being intersected have equal local
- * infos (or both have <code>null</code> local infos)
+ * @param localPrimary whether local variables are primary to
+ * the intersection; if <code>true</code>, then the only non-null
+ * results occur when registers being intersected have equal local infos (or
+ * both have <code>null</code> local infos)
* @return null-ok; the intersection
*/
public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
@@ -439,8 +346,7 @@ public final class RegisterSpec
}
LocalItem resultLocal =
- ((local == null) || !local.equals(other.getLocalItem()))
- ? null : local;
+ ((local == null) || !local.equals(other.getLocalItem())) ? null : local;
boolean sameName = (resultLocal == local);
if (localPrimary && !sameName) {
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
index adc77c35e..4eb2f65e8 100644
--- a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
@@ -187,47 +187,16 @@ public final class RegisterSpecSet
}
/**
- * Returns the spec in this set that's currently associated with a
- * given local (type, name, and signature), or {@code null} if there is
- * none. This ignores the register number of the given spec but
- * matches on everything else.
- *
- * @param spec non-null; local to look for
- * @return null-ok; first register found that matches, if any
- */
- public RegisterSpec findMatchingLocal(RegisterSpec spec) {
- int length = specs.length;
-
- for (int reg = 0; reg < length; reg++) {
- RegisterSpec s = specs[reg];
-
- if (s == null) {
- continue;
- }
-
- if (spec.matchesVariable(s)) {
- return s;
- }
- }
-
- return null;
- }
-
- /**
* Returns the spec in this set that's currently associated with a given
- * local (name and signature), or {@code null} if there is none.
+ * name, or null if there is none.
*
* @param local non-null; local item to search for
- * @return null-ok; first register found with matching name and signature
+ * @return null-ok; first register found with name.
*/
public RegisterSpec localItemToSpec(LocalItem local) {
- int length = specs.length;
-
- for (int reg = 0; reg < length; reg++) {
- RegisterSpec spec = specs[reg];
-
- if ((spec != null) && local.equals(spec.getLocalItem())) {
- return spec;
+ for (int reg = 0; reg < specs.length; reg++) {
+ if (specs[reg] != null && local.equals(specs[reg].getLocalItem())) {
+ return specs[reg];
}
}
@@ -291,22 +260,6 @@ public final class RegisterSpecSet
}
/**
- * Put the entire contents of the given set into this one.
- *
- * @param set non-null; the set to put into this instance
- */
- public void putAll(RegisterSpecSet set) {
- int max = set.getMaxSize();
-
- for (int i = 0; i < max; i++) {
- RegisterSpec spec = set.get(i);
- if (spec != null) {
- put(spec);
- }
- }
- }
-
- /**
* Intersects this instance with the given one, modifying this
* instance. The intersection consists of the pairwise
* {@link RegisterSpec#intersect} of corresponding elements from