CodeItem.java revision c604ed6c1a306ae963500fc63177bc9b6ae5569a
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.Code.InstructionReader;
32import org.jf.dexlib.Code.InstructionIterator;
33import org.jf.dexlib.Code.Opcode;
34import org.jf.dexlib.Code.InstructionWriter;
35import org.jf.dexlib.Util.AnnotatedOutput;
36import org.jf.dexlib.Util.Input;
37import org.jf.dexlib.Util.SparseArray;
38import org.jf.dexlib.Util.Leb128Utils;
39
40import java.util.List;
41
42public class CodeItem extends Item<CodeItem> {
43    private int registerCount;
44    private int inWords;
45    private int outWords;
46    private DebugInfoItem debugInfo;
47    private byte[] encodedInstructions;
48    private Item[] referencedItems;
49    private TryItem[] tries;
50    private EncodedCatchHandler[] encodedCatchHandlers;
51
52    private ClassDataItem.EncodedMethod parent;
53
54    /**
55     * Creates a new uninitialized <code>CodeItem</code>
56     * @param dexFile The <code>DexFile</code> that this item belongs to
57     */
58    public CodeItem(DexFile dexFile) {
59        super(dexFile);
60    }
61
62    /**
63     * Creates a new <code>CodeItem</code> with the given values.
64     * @param dexFile The <code>DexFile</code> that this item belongs to
65     * @param registerCount the number of registers that the method containing this code uses
66     * @param inWords the number of 2-byte words that the parameters to the method containing this code take
67     * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
68     * @param debugInfo the debug information for this code/method
69     * @param encodedInstructions the instructions, encoded as a byte array
70     * @param referencedItems an array of the items referenced by instructions, in order of occurance in the code
71     * @param tries an array of the tries defined for this code/method
72     * @param encodedCatchHandlers an array of the exception handlers defined for this code/method
73     */
74    private CodeItem(DexFile dexFile,
75                    int registerCount,
76                    int inWords,
77                    int outWords,
78                    DebugInfoItem debugInfo,
79                    byte[] encodedInstructions,
80                    Item[] referencedItems,
81                    TryItem[] tries,
82                    EncodedCatchHandler[] encodedCatchHandlers) {
83        super(dexFile);
84
85        this.registerCount = registerCount;
86        this.inWords = inWords;
87        this.outWords = outWords;
88        this.debugInfo = debugInfo;
89        if (debugInfo != null) {
90            debugInfo.setParent(this);
91        }
92        this.encodedInstructions = encodedInstructions;
93        this.referencedItems = referencedItems;
94        this.tries = tries;
95        this.encodedCatchHandlers = encodedCatchHandlers;
96    }
97
98    /**
99     * Returns a new <code>CodeItem</code> with the given values.
100     * @param dexFile The <code>DexFile</code> that this item belongs to
101     * @param registerCount the number of registers that the method containing this code uses
102     * @param inWords the number of 2-byte words that the parameters to the method containing this code take
103     * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
104     * @param debugInfo the debug information for this code/method
105     * @param encodedInstructions the instructions, encoded as a byte array
106     * @param referencedItems a list of the items referenced by instructions, in order of occurance in the code,
107     * or null if none
108     * @param tries a list of the tries defined for this code/method or null if none
109     * @param encodedCatchHandlers a list of the exception handlers defined for this code/method or null if none
110     * @return a new <code>CodeItem</code> with the given values.
111     */
112    public static CodeItem getInternedCodeItem(DexFile dexFile,
113                    int registerCount,
114                    int inWords,
115                    int outWords,
116                    DebugInfoItem debugInfo,
117                    byte[] encodedInstructions,
118                    List<Item> referencedItems,
119                    List<TryItem> tries,
120                    List<EncodedCatchHandler> encodedCatchHandlers) {
121        Item[] referencedItemsArray = null;
122        TryItem[] triesArray = null;
123        EncodedCatchHandler[] encodedCatchHandlersArray = null;
124
125        if (referencedItems != null && referencedItems.size() > 0) {
126            referencedItemsArray = new Item[referencedItems.size()];
127            referencedItems.toArray(referencedItemsArray);
128        }
129
130        if (tries != null && tries.size() > 0) {
131            triesArray = new TryItem[tries.size()];
132            tries.toArray(triesArray);
133        }
134
135        if (encodedCatchHandlers != null && encodedCatchHandlers.size() > 0) {
136            encodedCatchHandlersArray = new EncodedCatchHandler[encodedCatchHandlers.size()];
137            encodedCatchHandlers.toArray(encodedCatchHandlersArray);
138        }
139
140        CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, encodedInstructions,
141                referencedItemsArray, triesArray, encodedCatchHandlersArray);
142        return dexFile.CodeItemsSection.intern(codeItem);
143    }
144
145    /** {@inheritDoc} */
146    protected void readItem(Input in, ReadContext readContext) {
147        this.registerCount = in.readShort();
148        this.inWords = in.readShort();
149        this.outWords = in.readShort();
150        int triesCount = in.readShort();
151        this.debugInfo = (DebugInfoItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM,
152                in.readInt());
153        if (this.debugInfo != null) {
154            this.debugInfo.setParent(this);
155        }
156        int instructionCount = in.readInt();
157        this.encodedInstructions = in.readBytes(instructionCount * 2);
158        this.referencedItems = InstructionReader.getReferencedItems(encodedInstructions, dexFile);
159        if (triesCount > 0) {
160            in.alignTo(4);
161
162            //we need to read in the catch handlers first, so save the offset to the try items for future reference
163            int triesOffset = in.getCursor();
164            in.setCursor(triesOffset + 8 * triesCount);
165
166            //read in the encoded catch handlers
167            int encodedHandlerStart = in.getCursor();
168            int handlerCount = in.readUnsignedLeb128();
169            SparseArray<EncodedCatchHandler> handlerMap = new SparseArray<EncodedCatchHandler>(handlerCount);
170            encodedCatchHandlers = new EncodedCatchHandler[handlerCount];
171            for (int i=0; i<handlerCount; i++) {
172                int position = in.getCursor() - encodedHandlerStart;
173                encodedCatchHandlers[i] = new EncodedCatchHandler(dexFile, in);
174                handlerMap.append(position, encodedCatchHandlers[i]);
175            }
176            int codeItemEnd = in.getCursor();
177
178            //now go back and read the tries
179            in.setCursor(triesOffset);
180            tries = new TryItem[triesCount];
181            for (int i=0; i<triesCount; i++) {
182                tries[i] = new TryItem(in, handlerMap);
183            }
184
185            //and now back to the end of the code item
186            in.setCursor(codeItemEnd);
187        }
188    }
189
190    /** {@inheritDoc} */
191    protected int placeItem(int offset) {
192        offset += 16 + encodedInstructions.length;
193        if (tries != null && tries.length > 0) {
194            if (encodedInstructions.length % 4 != 0) {
195                offset+=2;
196            }
197
198            offset += tries.length * 8;
199            int encodedCatchHandlerBaseOffset = offset;
200            offset += Leb128Utils.unsignedLeb128Size(encodedCatchHandlers.length);
201            for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
202                offset = encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset);
203            }
204        }
205        return offset;
206    }
207
208    /** {@inheritDoc} */
209    protected void writeItem(final AnnotatedOutput out) {
210        if (out.annotates()) {
211            out.annotate(0, parent.method.getMethodString());
212            out.annotate(2, "registers_size: 0x" + Integer.toHexString(registerCount) + " (" + registerCount + ")");
213            out.annotate(2, "ins_size: 0x" + Integer.toHexString(inWords) + " (" + inWords + ")");
214            out.annotate(2, "outs_size: 0x" + Integer.toHexString(outWords) + " (" + outWords + ")");
215            int triesLength = tries==null?0:tries.length;
216            out.annotate(2, "tries_size: 0x" + Integer.toHexString(triesLength) + " (" + triesLength + ")");
217            if (debugInfo == null) {
218                out.annotate(4, "debug_info_off:");
219            } else {
220                out.annotate(4, "debug_info_off: 0x" + debugInfo.getOffset());
221            }
222            out.annotate(4, "insns_size: 0x" + Integer.toHexString(encodedInstructions.length / 2) + " (" +
223                    (encodedInstructions.length / 2) + ")");
224            InstructionIterator.IterateInstructions(encodedInstructions,
225                    new InstructionIterator.ProcessRawInstructionDelegate() {
226
227                        public void ProcessNormalInstruction(Opcode opcode, int index) {
228                            out.annotate(opcode.format.size, "[0x" + Integer.toHexString(index/2) + "] " + opcode.name +
229                                    " instruction");
230                        }
231
232                        public void ProcessReferenceInstruction(Opcode opcode, int index) {
233                            out.annotate(opcode.format.size, "[0x" + Integer.toHexString(index/2) + "] " + opcode.name +
234                                    " instruction");
235                        }
236
237                        public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
238                            out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
239                                    "packed_switch instruction");
240                        }
241
242                        public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
243                            out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
244                                    "sparse_switch instruction");
245                        }
246
247                        public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount,
248                                                                    int instructionLength) {
249                            out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
250                                    "fill_array_data instruction");
251                        }
252                    });
253        }
254
255        out.writeShort(registerCount);
256        out.writeShort(inWords);
257        out.writeShort(outWords);
258        if (tries == null) {
259            out.writeShort(0);
260        } else {
261            out.writeShort(tries.length);
262        }
263        if (debugInfo == null) {
264            out.writeInt(0);
265        } else {
266            out.writeInt(debugInfo.getOffset());
267        }
268        out.writeInt(encodedInstructions.length / 2);
269        InstructionWriter.writeInstructions(encodedInstructions, referencedItems, out);
270
271        if (tries != null && tries.length > 0) {
272            if (out.annotates()) {
273                if ((encodedInstructions.length % 4) != 0) {
274                    out.annotate("padding");
275                    out.writeShort(0);
276                }
277
278                int index = 0;
279                for (TryItem tryItem: tries) {
280                    out.annotate(0, "[0x" + Integer.toHexString(index++) + "] try_item");
281                    out.indent();
282                    tryItem.writeTo(out);
283                    out.deindent();
284                }
285
286                out.annotate("handler_count: 0x" + Integer.toHexString(encodedCatchHandlers.length) + "(" +
287                        encodedCatchHandlers.length + ")");
288                out.writeUnsignedLeb128(encodedCatchHandlers.length);
289
290                index = 0;
291                for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
292                    out.annotate(0, "[" + Integer.toHexString(index++) + "] encoded_catch_handler");
293                    out.indent();
294                    encodedCatchHandler.writeTo(out);
295                    out.deindent();
296                }
297            } else {
298                if ((encodedInstructions.length % 4) != 0) {
299                    out.writeShort(0);
300                }
301
302                for (TryItem tryItem: tries) {
303                    tryItem.writeTo(out);
304                }
305
306                out.writeUnsignedLeb128(encodedCatchHandlers.length);
307
308                for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
309                    encodedCatchHandler.writeTo(out);
310                }
311            }
312        }
313    }
314
315    /** {@inheritDoc} */
316    public ItemType getItemType() {
317        return ItemType.TYPE_CODE_ITEM;
318    }
319
320    /** {@inheritDoc} */
321    public String getConciseIdentity() {
322        //TODO: should mention the method name here
323        return "code_item @0x" + Integer.toHexString(getOffset());
324    }
325
326    /** {@inheritDoc} */
327    public int compareTo(CodeItem other) {
328        if (parent == null) {
329            if (other.parent == null) {
330                return 0;
331            }
332            return -1;
333        }
334        if (other.parent == null) {
335            return 1;
336        }
337        return parent.method.compareTo(other.parent.method);
338    }
339
340    /**
341     * @return the register count
342     */
343    public int getRegisterCount() {
344        return registerCount;
345    }
346
347    /**
348     * @return a byte array containing the encoded instructions
349     */
350    public byte[] getEncodedInstructions() {
351        return encodedInstructions;
352    }
353
354    /**
355     * @return an array of the <code>TryItem</code> objects in this <code>CodeItem</code>
356     */
357    public TryItem[] getTries() {
358        return tries;
359    }
360
361    /**
362     * @return the <code>DebugInfoItem</code> associated with this <code>CodeItem</code>
363     */
364    public DebugInfoItem getDebugInfo() {
365        return debugInfo;
366    }
367
368    /**
369     * Sets the <code>MethodIdItem</code> of the method that this <code>CodeItem</code> is associated with
370     * @param encodedMethod the <code>EncodedMethod</code> of the method that this <code>CodeItem</code> is associated
371     * with
372     */
373    protected void setParent(ClassDataItem.EncodedMethod encodedMethod) {
374        this.parent = encodedMethod;
375    }
376
377    /**
378     * @return the MethodIdItem of the method that this CodeItem belongs to
379     */
380    public ClassDataItem.EncodedMethod getParent() {
381        return parent;
382    }
383
384    public static class TryItem {
385        /**
386         * The address (in 2-byte words) within the code where the try block starts
387         */
388        public final int startAddress;
389
390        /**
391         * The number of 2-byte words that the try block covers
392         */
393        public final int instructionCount;
394
395        /**
396         * The associated exception handler
397         */
398        public final EncodedCatchHandler encodedCatchHandler;
399
400        /**
401         * Construct a new <code>TryItem</code> with the given values
402         * @param startAddress the address (in 2-byte words) within the code where the try block starts
403         * @param instructionCount the number of 2-byte words that the try block covers
404         * @param encodedCatchHandler the associated exception handler
405         */
406        public TryItem(int startAddress, int instructionCount, EncodedCatchHandler encodedCatchHandler) {
407            this.startAddress = startAddress;
408            this.instructionCount = instructionCount;
409            this.encodedCatchHandler = encodedCatchHandler;
410        }
411
412        /**
413         * This is used internally to construct a new <code>TryItem</code> while reading in a <code>DexFile</code>
414         * @param in the Input object to read the <code>TryItem</code> from
415         * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this <code>CodeItem</code>. The
416         * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list
417         * structure.
418         */
419        private TryItem(Input in, SparseArray<EncodedCatchHandler> encodedCatchHandlers) {
420            startAddress = in.readInt();
421            instructionCount = in.readShort();
422
423            encodedCatchHandler = encodedCatchHandlers.get(in.readShort());
424            if (encodedCatchHandler == null) {
425                throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem");
426            }
427        }
428
429        /**
430         * Writes the <code>TryItem</code> to the given <code>AnnotatedOutput</code> object
431         * @param out the <code>AnnotatedOutput</code> object to write to
432         */
433        private void writeTo(AnnotatedOutput out) {
434            if (out.annotates()) {
435                out.annotate(4, "start_addr: 0x" + Integer.toHexString(startAddress));
436                out.annotate(2, "insn_count: 0x" + Integer.toHexString(instructionCount) + " (" + instructionCount +
437                        ")");
438                out.annotate(2, "handler_off: 0x" + Integer.toHexString(encodedCatchHandler.getOffsetInList()));
439            }
440
441            out.writeInt(startAddress);
442            out.writeShort(instructionCount);
443            out.writeShort(encodedCatchHandler.getOffsetInList());
444        }
445    }
446
447    public static class EncodedCatchHandler {
448        /**
449         * An array of the individual exception handlers
450         */
451        public final EncodedTypeAddrPair[] handlers;
452
453        /**
454         * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all
455         * handler
456         */
457        public final int catchAllHandlerAddress;
458
459        //TODO: would it be possible to get away without having these? and generate/create these values while writing?
460        private int baseOffset;
461        private int offset;
462
463        /**
464         * Constructs a new <code>EncodedCatchHandler</code> with the given values
465         * @param handlers an array of the individual exception handlers
466         * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1
467         * if there is no catch all handler
468         */
469        public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) {
470            this.handlers = handlers;
471            this.catchAllHandlerAddress = catchAllHandlerAddress;
472        }
473
474        /**
475         * This is used internally to construct a new <code>EncodedCatchHandler</code> while reading in a
476         * <code>DexFile</code>
477         * @param dexFile the <code>DexFile</code> that is being read in
478         * @param in the Input object to read the <code>EncodedCatchHandler</code> from
479         */
480        private EncodedCatchHandler(DexFile dexFile, Input in) {
481            int handlerCount = in.readSignedLeb128();
482
483            if (handlerCount < 0) {
484                handlers = new EncodedTypeAddrPair[-1 * handlerCount];
485            } else {
486                handlers = new EncodedTypeAddrPair[handlerCount];
487            }
488
489            for (int i=0; i<handlers.length; i++) {
490                handlers[i] = new EncodedTypeAddrPair(dexFile, in);
491            }
492
493            if (handlerCount <= 0) {
494                catchAllHandlerAddress = in.readUnsignedLeb128();
495            } else {
496                catchAllHandlerAddress = -1;
497            }
498        }
499
500        /**
501         * @return the offset of this <code>EncodedCatchHandler</code> from the beginning of the
502         * encoded_catch_handler_list structure
503         */
504        private int getOffsetInList() {
505            return offset-baseOffset;
506        }
507
508        /**
509         * Places the <code>EncodedCatchHandler</code>, storing the offset and baseOffset, and returning the offset
510         * immediately following this <code>EncodedCatchHandler</code>
511         * @param offset the offset of this <code>EncodedCatchHandler</code> in the <code>DexFile</code>
512         * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the
513         * <code>DexFile</code>
514         * @return the offset immediately following this <code>EncodedCatchHandler</code>
515         */
516        private int place(int offset, int baseOffset) {
517            this.offset = offset;
518            this.baseOffset = baseOffset;
519
520            int size = handlers.length;
521            if (catchAllHandlerAddress > -1) {
522                size *= -1;
523                offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress);
524            }
525            offset += Leb128Utils.signedLeb128Size(size);
526
527            for (EncodedTypeAddrPair handler: handlers) {
528                offset += handler.getSize();
529            }
530            return offset;
531        }
532
533        /**
534         * Writes the <code>EncodedCatchHandler</code> to the given <code>AnnotatedOutput</code> object
535         * @param out the <code>AnnotatedOutput</code> object to write to
536         */
537        private void writeTo(AnnotatedOutput out) {
538            if (out.annotates()) {
539                out.annotate("size: 0x" + Integer.toHexString(handlers.length) + " (" + handlers.length + ")");
540
541                int size = handlers.length;
542                if (catchAllHandlerAddress > -1) {
543                    size = size * -1;
544                }
545                out.writeSignedLeb128(size);
546
547                int index = 0;
548                for (EncodedTypeAddrPair handler: handlers) {
549                    out.annotate(0, "[" + index++ + "] encoded_type_addr_pair");
550                    out.indent();
551                    handler.writeTo(out);
552                    out.deindent();
553                }
554
555                if (catchAllHandlerAddress > -1) {
556                    out.annotate("catch_all_addr: 0x" + Integer.toHexString(catchAllHandlerAddress));
557                    out.writeUnsignedLeb128(catchAllHandlerAddress);
558                }
559            } else {
560                int size = handlers.length;
561                if (catchAllHandlerAddress > -1) {
562                    size = size * -1;
563                }
564                out.writeSignedLeb128(size);
565
566                for (EncodedTypeAddrPair handler: handlers) {
567                    handler.writeTo(out);
568                }
569
570                if (catchAllHandlerAddress > -1) {
571                    out.writeUnsignedLeb128(catchAllHandlerAddress);
572                }
573            }
574        }
575
576        @Override
577        public int hashCode() {
578            int hash = 0;
579            for (EncodedTypeAddrPair handler: handlers) {
580                hash = hash * 31 + handler.hashCode();
581            }
582            hash = hash * 31 + catchAllHandlerAddress;
583            return hash;
584        }
585
586        @Override
587        public boolean equals(Object o) {
588            if (this==o) {
589                return true;
590            }
591            if (o==null || !this.getClass().equals(o.getClass())) {
592                return false;
593            }
594
595            EncodedCatchHandler other = (EncodedCatchHandler)o;
596            if (handlers.length != other.handlers.length || catchAllHandlerAddress != other.catchAllHandlerAddress) {
597                return false;
598            }
599
600            for (int i=0; i<handlers.length; i++) {
601                if (!handlers[i].equals(other.handlers[i])) {
602                    return false;
603                }
604            }
605
606            return true;
607        }
608    }
609
610    public static class EncodedTypeAddrPair {
611        /**
612         * The type of the <code>Exception</code> that this handler handles
613         */
614        public final TypeIdItem exceptionType;
615
616        /**
617         * The address (in 2-byte words) in the code of the handler
618         */
619        public final int handlerAddress;
620
621        /**
622         * Constructs a new <code>EncodedTypeAddrPair</code> with the given values
623         * @param exceptionType the type of the <code>Exception</code> that this handler handles
624         * @param handlerAddress the address (in 2-byte words) in the code of the handler
625         */
626        public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) {
627            this.exceptionType = exceptionType;
628            this.handlerAddress = handlerAddress;
629        }
630
631        /**
632         * This is used internally to construct a new <code>EncodedTypeAddrPair</code> while reading in a
633         * <code>DexFile</code>
634         * @param dexFile the <code>DexFile</code> that is being read in
635         * @param in the Input object to read the <code>EncodedCatchHandler</code> from
636         */
637        private EncodedTypeAddrPair(DexFile dexFile, Input in) {
638            exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128());
639            handlerAddress = in.readUnsignedLeb128();
640        }
641
642        /**
643         * @return the size of this <code>EncodedTypeAddrPair</code>
644         */
645        private int getSize() {
646            return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) +
647                   Leb128Utils.unsignedLeb128Size(handlerAddress);
648        }
649
650        /**
651         * Writes the <code>EncodedTypeAddrPair</code> to the given <code>AnnotatedOutput</code> object
652         * @param out the <code>AnnotatedOutput</code> object to write to
653         */
654        private void writeTo(AnnotatedOutput out) {
655            if (out.annotates()) {
656                out.annotate("exception_type: " + exceptionType.getTypeDescriptor());
657                out.writeUnsignedLeb128(exceptionType.getIndex());
658
659                out.annotate("handler_addr: 0x" + Integer.toHexString(handlerAddress));
660                out.writeUnsignedLeb128(handlerAddress);
661            } else {
662                out.writeUnsignedLeb128(exceptionType.getIndex());
663                out.writeUnsignedLeb128(handlerAddress);
664            }
665        }
666
667        @Override
668        public int hashCode() {
669            return exceptionType.hashCode() * 31 + handlerAddress;
670        }
671
672        @Override
673        public boolean equals(Object o) {
674            if (this==o) {
675                return true;
676            }
677            if (o==null || !this.getClass().equals(o.getClass())) {
678                return false;
679            }
680
681            EncodedTypeAddrPair other = (EncodedTypeAddrPair)o;
682            return exceptionType == other.exceptionType && handlerAddress == other.handlerAddress;
683        }
684    }
685}
686