CodeItem.java revision b3fde8be3020e84010cc820d0cc16e2955755353
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.InstructionField;
32import org.jf.dexlib.code.Opcode;
33import org.jf.dexlib.util.AnnotatedOutput;
34import org.jf.dexlib.util.Input;
35
36import java.util.ArrayList;
37import java.util.HashMap;
38import java.util.List;
39
40public class CodeItem extends OffsettedItem<CodeItem> {
41    private final ArrayList<InstructionField> instructionList;
42    private final ArrayList<TryItem> tryItems = new ArrayList<TryItem>();
43    private final ArrayList<EncodedCatchHandler> catchHandlerList = new ArrayList<EncodedCatchHandler>();
44
45    private final ShortIntegerField registersCountField;
46    private final ShortIntegerField inArgumentCountField;
47    private final ShortIntegerField outArgumentCountField;
48    private final ListSizeField triesCountField;
49    private final OffsettedItemReference<DebugInfoItem> debugInfoReferenceField;
50    private final IntegerField instructionsSizeField;
51    private final InstructionListField instructionListField;
52    private final PaddingField paddingField;
53    private final FieldListField<TryItem> triesListField;
54    private final EncodedCatchHandlerList catchHandlersListField;
55
56    public CodeItem(final DexFile dexFile, int offset) {
57        super(offset);
58
59        instructionList = new ArrayList<InstructionField>();
60
61        fields = new Field[] {
62                registersCountField = new ShortIntegerField("registers_size"),
63                inArgumentCountField = new ShortIntegerField("ins_size"),
64                outArgumentCountField = new ShortIntegerField("outs_size"),
65                triesCountField = new ListSizeField(tryItems, new ShortIntegerField("tries_size")),
66                debugInfoReferenceField = new OffsettedItemReference<DebugInfoItem>(dexFile.DebugInfoItemsSection,
67                        new IntegerField(null), "debug_off"),
68                instructionsSizeField = new IntegerField("insns_size"),
69                instructionListField = new InstructionListField(dexFile),
70                paddingField = new PaddingField(),
71                triesListField = new FieldListField<TryItem>(tryItems, "try_item") {
72                    protected TryItem make() {
73                        return new TryItem(catchHandlersListField);
74                    }
75                },
76
77                catchHandlersListField = new EncodedCatchHandlerList(dexFile)
78        };
79    }
80
81    public CodeItem(final DexFile dexFile,
82                    int registersCount,
83                    int inArguments,
84                    List<InstructionField> instructions,
85                    DebugInfoItem debugInfo,
86                    List<TryItem> tries,
87                    List<EncodedCatchHandler> handlers) {
88        this(dexFile, 0);
89
90        instructionList.addAll(instructions);
91        instructionsSizeField.cacheValue(instructionListField.getInstructionWordCount());
92
93        if (tries != null) {
94            tryItems.addAll(tries);
95            if (handlers == null) {
96                throw new RuntimeException("The handlers parameter cannot be null if tries parameter is not null");
97            }
98            catchHandlerList.addAll(handlers);
99        } else if (handlers != null) {
100            throw new RuntimeException("The handlers parameter must be null if the tries parameter is null");
101        }
102
103        registersCountField.cacheValue(registersCount);
104        inArgumentCountField.cacheValue(inArguments);
105        outArgumentCountField.cacheValue(instructionListField.getOutArguments());
106        debugInfoReferenceField.setReference(debugInfo);
107    }
108
109    protected int getAlignment() {
110        return 4;
111    }
112
113    public ItemType getItemType() {
114        return ItemType.TYPE_CODE_ITEM;
115    }
116
117    public int getRegisterCount() {
118        return registersCountField.getCachedValue();
119    }
120
121    public List<InstructionField> getInstructions() {
122        return (List<InstructionField>)instructionList.clone();
123    }
124
125    public List<TryItem> getTries() {
126        return (List<TryItem>)tryItems.clone();
127    }
128
129    public DebugInfoItem getDebugInfo() {
130        return debugInfoReferenceField.getReference();
131    }
132
133    public void copyTo(DexFile dexFile, CodeItem copy)
134    {
135        for (int i = 0; i < fields.length-2; i++) {
136            fields[i].copyTo(dexFile, copy.fields[i]);
137        }
138        //we need to do this in reverse order, so when the tries are copied,
139        //the catchHandler copies will already exist
140        catchHandlersListField.copyTo(dexFile, copy.catchHandlersListField);
141        triesListField.copyTo(dexFile, copy.triesListField);
142    }
143
144    public String getConciseIdentity() {
145        //TODO: should mention the method name here
146        return "code_item @0x" + Integer.toHexString(getOffset());
147    }
148
149    public static class TryItem extends CompositeField<TryItem> {
150        private final IntegerField startAddr;
151        private final ShortIntegerField insnCount;
152        private final EncodedCatchHandlerReference encodedCatchHandlerReference;
153
154        public TryItem(EncodedCatchHandlerList encodedCatchHandlerList) {
155            super("try_item");
156            fields = new Field[] {
157                    startAddr = new IntegerField("start_addr"),
158                    insnCount = new ShortIntegerField("insn_count"),
159                    encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandlerList)
160            };
161        }
162
163        public TryItem(int startAddr, int insnCount, EncodedCatchHandler encodedCatchHandler) {
164            super("try_item");
165            fields = new Field[] {
166                    this.startAddr = new IntegerField(startAddr, "start_addr"),
167                    this.insnCount = new ShortIntegerField(insnCount, "insn_count"),
168                    this.encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandler)
169            };
170        }
171
172        public int getStartAddress() {
173            return startAddr.getCachedValue();
174        }
175
176        public int getEndAddress() {
177            return startAddr.getCachedValue() + insnCount.getCachedValue();
178        }
179
180        public EncodedCatchHandler getHandler() {
181            return encodedCatchHandlerReference.getReference();
182        }
183    }
184
185    public static class EncodedCatchHandlerReference extends ShortIntegerField {
186        private final EncodedCatchHandlerList encodedCatchHandlerList;
187        private EncodedCatchHandler encodedCatchHandler;
188
189        public EncodedCatchHandlerReference(EncodedCatchHandlerList encodedCatchHandlerList) {
190            super("encoded_catch_handler");
191            this.encodedCatchHandlerList = encodedCatchHandlerList;
192        }
193
194        public EncodedCatchHandlerReference(EncodedCatchHandler encodedCatchHandler) {
195            super("encoded_catch_handler");
196            this.encodedCatchHandlerList = null;
197            this.encodedCatchHandler = encodedCatchHandler;
198        }
199
200        public EncodedCatchHandlerList getEncodedCatchHandlerList() {
201            return encodedCatchHandlerList;
202        }
203
204        private void setReference(EncodedCatchHandler encodedCatchHandler) {
205            this.encodedCatchHandler = encodedCatchHandler;
206        }
207
208        public EncodedCatchHandler getReference() {
209            return encodedCatchHandler;
210        }
211
212        public void copyTo(DexFile dexFile, CachedIntegerValueField _copy) {
213            EncodedCatchHandlerReference copy = (EncodedCatchHandlerReference)_copy;
214            EncodedCatchHandler copiedItem = copy.getEncodedCatchHandlerList().getByOffset(
215                encodedCatchHandler.getOffsetInList());
216            copy.setReference(copiedItem);
217        }
218
219
220        public void writeTo(AnnotatedOutput out) {
221            cacheValue(encodedCatchHandler.getOffsetInList());
222
223            super.writeTo(out);
224        }
225
226        public void readFrom(Input in) {
227            super.readFrom(in);
228
229            encodedCatchHandler = encodedCatchHandlerList.getByOffset(getCachedValue());
230        }
231
232        public int place(int offset) {
233            cacheValue(encodedCatchHandler.getOffsetInList());
234            return super.place(offset);
235        }
236    }
237
238    public class EncodedCatchHandlerList extends CompositeField<EncodedCatchHandlerList> {
239        private boolean fieldPresent = false;
240        protected HashMap<Integer, EncodedCatchHandler> itemsByOffset =
241                new HashMap<Integer, EncodedCatchHandler>();
242
243        private final DexFile dexFile;
244
245        public EncodedCatchHandler getByOffset(int offset) {
246            EncodedCatchHandler encodedCatchHandler = itemsByOffset.get(offset);
247            if (encodedCatchHandler == null) {
248                encodedCatchHandler = new EncodedCatchHandler(dexFile, offset);
249                itemsByOffset.put(offset, encodedCatchHandler);
250            }
251            return encodedCatchHandler;
252
253
254        }
255
256        public EncodedCatchHandlerList(final DexFile dexFile) {
257            super("encoded_catch_handler_list");
258            this.dexFile = dexFile;
259
260            fields = new Field[] {
261                sizeField = new ListSizeField(catchHandlerList, new Leb128Field("size")),
262                listField = new FieldListField<EncodedCatchHandler>(catchHandlerList, "encoded_catch_handler") {
263                    protected EncodedCatchHandler make() {
264                        return new EncodedCatchHandler(dexFile, 0);
265                    }
266
267                    public void readFrom(Input in) {
268                        int currentOffset = sizeField.place(0);
269
270                        for (int i = 0; i < list.size(); i++) {
271                                EncodedCatchHandler field = list.get(i);
272
273                                if (field == null) {
274                                    field = itemsByOffset.get(currentOffset);
275                                    if (field == null) {
276                                        field = new EncodedCatchHandler(dexFile, currentOffset);
277                                    }
278                                    list.set(i, field);
279                                }
280                                int savedOffset = in.getCursor();
281                                field.readFrom(in);
282                                currentOffset += in.getCursor() - savedOffset;
283                            }
284                        }
285                   }
286             };
287        }
288
289        private final ListSizeField sizeField;
290        private final FieldListField<EncodedCatchHandler> listField;
291
292        public void readFrom(Input in) {
293            if (tryItems.size() > 0) {
294                fieldPresent = true;
295                super.readFrom(in);
296            }
297        }
298
299        public void writeTo(AnnotatedOutput out) {
300            if (fieldPresent) {
301                super.writeTo(out);
302            }
303        }
304
305        public int place(int offset) {
306            for (EncodedCatchHandler encodedCatchHandler: listField.list) {
307                encodedCatchHandler.setBaseOffset(offset);
308            }
309            if (tryItems.size() > 0) {
310                fieldPresent = true;
311                return super.place(offset);
312            } else {
313                return offset;
314            }
315        }
316
317        public void copyTo(DexFile dexFile, EncodedCatchHandlerList copy) {
318            super.copyTo(dexFile, copy);
319            copy.fieldPresent = fieldPresent;
320            copy.itemsByOffset.clear();
321            for (EncodedCatchHandler encodedCatchHandler: copy.listField.list) {
322                copy.itemsByOffset.put(encodedCatchHandler.offset, encodedCatchHandler);
323            }
324        }
325    }
326
327    public static class EncodedCatchHandler extends CompositeField<EncodedCatchHandler> {
328        private ArrayList<EncodedTypeAddrPair> list;
329        boolean hasCatchAll = false;
330        private int baseOffset = 0;
331
332        private final ListSizeField size;
333        private final FieldListField<EncodedTypeAddrPair> handlers;
334        private final Leb128Field catchAllAddress;
335
336        private int offset;
337
338        public EncodedCatchHandler(final DexFile dexFile, int offset) {
339            super("encoded_catch_handler");
340            this.offset = offset;
341
342            list = new ArrayList<EncodedTypeAddrPair>();
343            fields = new Field[] {
344                    size = new ListSizeField(list, new SignedLeb128Field("size") {
345                        public void readFrom(Input in) {
346                            super.readFrom(in);
347                            hasCatchAll = (getCachedValue() <= 0);
348                        }
349
350                        public void cacheValue(int value) {
351                            super.cacheValue(value * (hasCatchAll?-1:1));
352                        }})
353                    ,
354                    handlers = new FieldListField<EncodedTypeAddrPair>(list, "encoded_type_addr_pair") {
355                        protected EncodedTypeAddrPair make() {
356                            return new EncodedTypeAddrPair(dexFile);
357                        }
358                    },
359                    catchAllAddress = new Leb128Field("catch_all_addr") {
360                        public void readFrom(Input in) {
361                            if (hasCatchAll) {
362                                super.readFrom(in);
363                            }
364                        }
365
366                        public void writeTo(AnnotatedOutput out) {
367                            if (hasCatchAll) {
368                                super.writeTo(out);
369                            }
370                        }
371
372                        public int place(int offset) {
373                            if (hasCatchAll) {
374                                return super.place(offset);
375                            }
376                            return offset;
377                        }
378                    }
379            };
380        }
381
382        public EncodedCatchHandler(final DexFile dexFile, List<EncodedTypeAddrPair> handlers, int catchAllHandler) {
383            this(dexFile, 0);
384
385            list.addAll(handlers);
386            if (catchAllHandler >= 0) {
387                hasCatchAll = true;
388                catchAllAddress.cacheValue(catchAllHandler);
389            }
390        }
391
392        public int getOffsetInList() {
393            return offset-baseOffset;
394        }
395
396        public void setBaseOffset(int baseOffset) {
397            this.baseOffset = baseOffset;
398        }
399
400        public void copyTo(DexFile dexFile, EncodedCatchHandler copy) {
401            super.copyTo(dexFile, copy);
402            copy.hasCatchAll = hasCatchAll;
403            copy.offset = offset;
404        }
405
406        public int place(int offset) {
407            this.offset = offset;
408            return super.place(offset);
409        }
410
411        public int getCatchAllAddress() {
412            if (hasCatchAll) {
413                return catchAllAddress.getCachedValue();
414            } else {
415                return -1;
416            }
417        }
418
419        //TODO: GROT
420        public int getHandlerCount() {
421            return list.size();
422        }
423
424        //TODO: GROT
425        public EncodedTypeAddrPair getHandler(int index) {
426            return list.get(index);
427        }
428
429        public List<EncodedTypeAddrPair> getHandlers() {
430            return (List<EncodedTypeAddrPair>)list.clone();
431        }
432    }
433
434    public static class EncodedTypeAddrPair extends CompositeField<EncodedTypeAddrPair> {
435        public final IndexedItemReference<TypeIdItem> typeReferenceField;
436        public final Leb128Field handlerAddressField;
437
438        public EncodedTypeAddrPair(DexFile dexFile) {
439            super("encoded_type_addr_pair");
440            fields = new Field[] {
441                    typeReferenceField = new IndexedItemReference<TypeIdItem>(dexFile.TypeIdsSection,
442                            new Leb128Field(null), "type_idx"),
443                    handlerAddressField = new Leb128Field("addr")
444            };
445        }
446
447        public EncodedTypeAddrPair(DexFile dexFile, TypeIdItem type, int handlerOffset) {
448            this(dexFile);
449            typeReferenceField.setReference(type);
450            handlerAddressField.cacheValue(handlerOffset);
451        }
452
453        public TypeIdItem getTypeReferenceField() {
454            return typeReferenceField.getReference();
455        }
456
457        public int getHandlerAddress() {
458            return handlerAddressField.getCachedValue();
459        }
460    }
461
462    private class InstructionListField implements Field<InstructionListField> {
463        private final DexFile dexFile;
464
465        public InstructionListField(DexFile dexFile) {
466            this.dexFile = dexFile;
467        }
468
469        public void writeTo(AnnotatedOutput out) {
470            int startPosition = out.getCursor();
471            for (InstructionField instruction: instructionList) {
472                instruction.writeTo(out);
473            }
474            if ((out.getCursor() - startPosition) != (instructionsSizeField.getCachedValue() * 2)) {
475                throw new RuntimeException("Did not write the expected amount of bytes");
476            }
477        }
478
479        public void readFrom(Input in) {
480            int numBytes = instructionsSizeField.getCachedValue() * 2;
481            int startPosition = in.getCursor();
482
483            do {
484                InstructionField instruction = new InstructionField(dexFile);
485                instruction.readFrom(in);
486                instructionList.add(instruction);
487            } while (in.getCursor() - startPosition < numBytes);
488
489            if (in.getCursor() - startPosition != numBytes) {
490                throw new RuntimeException("Read past the end of the code section");
491            }
492        }
493
494        public int place(int offset) {
495            return offset + (instructionsSizeField.getCachedValue() * 2);
496        }
497
498        public void copyTo(DexFile dexFile, InstructionListField copy) {
499            ArrayList<InstructionField> copyInstructionList = copy.getInstructionList();
500            copyInstructionList.clear();
501            for (InstructionField instruction: instructionList) {
502                InstructionField instructionCopy = new InstructionField(dexFile);
503                instruction.copyTo(dexFile, instructionCopy);
504                copyInstructionList.add(instructionCopy);
505            }
506        }
507
508        private ArrayList<InstructionField> getInstructionList() {
509            return instructionList;
510        }
511
512        //return the word size of the instruction list
513        public int getInstructionWordCount() {
514            int bytes = 0;
515            for (InstructionField instruction: instructionList) {
516                bytes += instruction.getSize(bytes);
517            }
518            return bytes/2;
519        }
520
521        //return the highest parameter word count of any method invokation
522        public int getOutArguments() {
523            int maxParamWordCount = 0;
524            for (InstructionField instruction: instructionList) {
525                IndexedItem item = instruction.getInstruction().getReferencedItem();
526                if (item instanceof MethodIdItem) {
527                    MethodIdItem methodIdItem = (MethodIdItem)item;
528                    Opcode opcode = instruction.getInstruction().getOpcode();
529
530                    boolean isStatic = false;
531                    if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) {
532                        isStatic = true;
533                    }
534                    int paramWordCount = methodIdItem.getParameterRegisterCount(isStatic);
535
536                    if (maxParamWordCount < paramWordCount) {
537                        maxParamWordCount = paramWordCount;
538                    }
539                }
540            }
541            return maxParamWordCount;
542        }
543    }
544
545    private class PaddingField implements Field {
546
547        public PaddingField() {
548        }
549
550        private boolean needsAlign() {
551            return (triesCountField.getCachedValue() > 0) && (instructionsSizeField.getCachedValue() % 2 == 1);
552        }
553
554        public void writeTo(AnnotatedOutput out) {
555            if (needsAlign()) {
556                out.writeShort(0);
557            }
558        }
559
560        public void readFrom(Input in) {
561            if (needsAlign()) {
562                in.skipBytes(2);
563            }
564        }
565
566        public int place(int offset) {
567            if (needsAlign()) {
568                return offset + 2;
569            } else {
570                return offset;
571            }
572        }
573
574        public int hashCode() {
575            return 0;
576        }
577
578        public boolean equals(Object o) {
579            return getClass() == o.getClass();
580        }
581
582        public void copyTo(DexFile dexFile, Field field) {
583        }
584    }
585}
586