summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/dex/file/CatchStructs.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/dex/file/CatchStructs.java')
-rw-r--r--dx/src/com/android/dx/dex/file/CatchStructs.java317
1 files changed, 317 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/dex/file/CatchStructs.java b/dx/src/com/android/dx/dex/file/CatchStructs.java
new file mode 100644
index 0000000..e07ec29
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CatchStructs.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CatchHandlerList;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * List of exception handlers (tuples of covered range, catch type,
+ * handler address) for a particular piece of code. Instances of this
+ * class correspond to a {@code try_item[]} and a
+ * {@code catch_handler_item[]}.
+ */
+public final class CatchStructs {
+ /**
+ * the size of a {@code try_item}: a {@code uint}
+ * and two {@code ushort}s
+ */
+ private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2);
+
+ /** {@code non-null;} code that contains the catches */
+ private final DalvCode code;
+
+ /**
+ * {@code null-ok;} the underlying table; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private CatchTable table;
+
+ /**
+ * {@code null-ok;} the encoded handler list, if calculated; set in
+ * {@link #encode}
+ */
+ private byte[] encodedHandlers;
+
+ /**
+ * length of the handlers header (encoded size), if known; used for
+ * annotation
+ */
+ private int encodedHandlerHeaderSize;
+
+ /**
+ * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in
+ * {@link #encode}
+ */
+ private TreeMap<CatchHandlerList, Integer> handlerOffsets;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param code {@code non-null;} code that contains the catches
+ */
+ public CatchStructs(DalvCode code) {
+ this.code = code;
+ this.table = null;
+ this.encodedHandlers = null;
+ this.encodedHandlerHeaderSize = 0;
+ this.handlerOffsets = null;
+ }
+
+ /**
+ * Finish processing the catches, if necessary.
+ */
+ private void finishProcessingIfNecessary() {
+ if (table == null) {
+ table = code.getCatches();
+ }
+ }
+
+ /**
+ * Gets the size of the tries list, in entries.
+ *
+ * @return {@code >= 0;} the tries list size
+ */
+ public int triesSize() {
+ finishProcessingIfNecessary();
+ return table.size();
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ */
+ public void debugPrint(PrintWriter out, String prefix) {
+ annotateEntries(prefix, out, null);
+ }
+
+ /**
+ * Encodes the handler lists.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ */
+ public void encode(DexFile file) {
+ finishProcessingIfNecessary();
+
+ TypeIdsSection typeIds = file.getTypeIds();
+ int size = table.size();
+
+ handlerOffsets = new TreeMap<CatchHandlerList, Integer>();
+
+ /*
+ * First add a map entry for each unique list. The tree structure
+ * will ensure they are sorted when we reiterate later.
+ */
+ for (int i = 0; i < size; i++) {
+ handlerOffsets.put(table.get(i).getHandlers(), null);
+ }
+
+ if (handlerOffsets.size() > 65535) {
+ throw new UnsupportedOperationException(
+ "too many catch handlers");
+ }
+
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+ // Write out the handlers "header" consisting of its size in entries.
+ encodedHandlerHeaderSize =
+ out.writeUnsignedLeb128(handlerOffsets.size());
+
+ // Now write the lists out in order, noting the offset of each.
+ for (Map.Entry<CatchHandlerList, Integer> mapping :
+ handlerOffsets.entrySet()) {
+ CatchHandlerList list = mapping.getKey();
+ int listSize = list.size();
+ boolean catchesAll = list.catchesAll();
+
+ // Set the offset before we do any writing.
+ mapping.setValue(out.getCursor());
+
+ if (catchesAll) {
+ // A size <= 0 means that the list ends with a catch-all.
+ out.writeSignedLeb128(-(listSize - 1));
+ listSize--;
+ } else {
+ out.writeSignedLeb128(listSize);
+ }
+
+ for (int i = 0; i < listSize; i++) {
+ CatchHandlerList.Entry entry = list.get(i);
+ out.writeUnsignedLeb128(
+ typeIds.indexOf(entry.getExceptionType()));
+ out.writeUnsignedLeb128(entry.getHandler());
+ }
+
+ if (catchesAll) {
+ out.writeUnsignedLeb128(list.get(listSize).getHandler());
+ }
+ }
+
+ encodedHandlers = out.toByteArray();
+ }
+
+ /**
+ * Gets the write size of this instance, in bytes.
+ *
+ * @return {@code >= 0;} the write size
+ */
+ public int writeSize() {
+ return (triesSize() * TRY_ITEM_WRITE_SIZE) +
+ + encodedHandlers.length;
+ }
+
+ /**
+ * Writes this instance to the given stream.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ */
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ finishProcessingIfNecessary();
+
+ if (out.annotates()) {
+ annotateEntries(" ", null, out);
+ }
+
+ int tableSize = table.size();
+ for (int i = 0; i < tableSize; i++) {
+ CatchTable.Entry one = table.get(i);
+ int start = one.getStart();
+ int end = one.getEnd();
+ int insnCount = end - start;
+
+ if (insnCount >= 65536) {
+ throw new UnsupportedOperationException(
+ "bogus exception range: " + Hex.u4(start) + ".." +
+ Hex.u4(end));
+ }
+
+ out.writeInt(start);
+ out.writeShort(insnCount);
+ out.writeShort(handlerOffsets.get(one.getHandlers()));
+ }
+
+ out.write(encodedHandlers);
+ }
+
+ /**
+ * Helper method to annotate or simply print the exception handlers.
+ * Only one of {@code printTo} or {@code annotateTo} should
+ * be non-null.
+ *
+ * @param prefix {@code non-null;} prefix for each line
+ * @param printTo {@code null-ok;} where to print to
+ * @param annotateTo {@code null-ok;} where to consume bytes and annotate to
+ */
+ private void annotateEntries(String prefix, PrintWriter printTo,
+ AnnotatedOutput annotateTo) {
+ finishProcessingIfNecessary();
+
+ boolean consume = (annotateTo != null);
+ int amt1 = consume ? 6 : 0;
+ int amt2 = consume ? 2 : 0;
+ int size = table.size();
+ String subPrefix = prefix + " ";
+
+ if (consume) {
+ annotateTo.annotate(0, prefix + "tries:");
+ } else {
+ printTo.println(prefix + "tries:");
+ }
+
+ for (int i = 0; i < size; i++) {
+ CatchTable.Entry entry = table.get(i);
+ CatchHandlerList handlers = entry.getHandlers();
+ String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart())
+ + ".." + Hex.u2or4(entry.getEnd());
+ String s2 = handlers.toHuman(subPrefix, "");
+
+ if (consume) {
+ annotateTo.annotate(amt1, s1);
+ annotateTo.annotate(amt2, s2);
+ } else {
+ printTo.println(s1);
+ printTo.println(s2);
+ }
+ }
+
+ if (! consume) {
+ // Only emit the handler lists if we are consuming bytes.
+ return;
+ }
+
+ annotateTo.annotate(0, prefix + "handlers:");
+ annotateTo.annotate(encodedHandlerHeaderSize,
+ subPrefix + "size: " + Hex.u2(handlerOffsets.size()));
+
+ int lastOffset = 0;
+ CatchHandlerList lastList = null;
+
+ for (Map.Entry<CatchHandlerList, Integer> mapping :
+ handlerOffsets.entrySet()) {
+ CatchHandlerList list = mapping.getKey();
+ int offset = mapping.getValue();
+
+ if (lastList != null) {
+ annotateAndConsumeHandlers(lastList, lastOffset,
+ offset - lastOffset, subPrefix, printTo, annotateTo);
+ }
+
+ lastList = list;
+ lastOffset = offset;
+ }
+
+ annotateAndConsumeHandlers(lastList, lastOffset,
+ encodedHandlers.length - lastOffset,
+ subPrefix, printTo, annotateTo);
+ }
+
+ /**
+ * Helper for {@link #annotateEntries} to annotate a catch handler list
+ * while consuming it.
+ *
+ * @param handlers {@code non-null;} handlers to annotate
+ * @param offset {@code >= 0;} the offset of this handler
+ * @param size {@code >= 1;} the number of bytes the handlers consume
+ * @param prefix {@code non-null;} prefix for each line
+ * @param printTo {@code null-ok;} where to print to
+ * @param annotateTo {@code non-null;} where to annotate to
+ */
+ private static void annotateAndConsumeHandlers(CatchHandlerList handlers,
+ int offset, int size, String prefix, PrintWriter printTo,
+ AnnotatedOutput annotateTo) {
+ String s = handlers.toHuman(prefix, Hex.u2(offset) + ": ");
+
+ if (printTo != null) {
+ printTo.println(s);
+ }
+
+ annotateTo.annotate(size, s);
+ }
+}