DexBackedMethodImplementation.java revision 5ec83fb0f01281f60123b206ccec7b2a453ead93
1/*
2 * Copyright 2012, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.dexbacked;
33
34import com.google.common.collect.ImmutableList;
35import org.jf.dexlib2.DexFile;
36import org.jf.dexlib2.DexFileReader;
37import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
38import org.jf.dexlib2.dexbacked.util.FixedSizeList;
39import org.jf.dexlib2.dexbacked.util.InstructionOffsetMap;
40import org.jf.dexlib2.iface.MethodImplementation;
41import org.jf.dexlib2.iface.TryBlock;
42import org.jf.dexlib2.iface.instruction.Instruction;
43import org.jf.util.AlignmentUtils;
44
45import javax.annotation.Nonnull;
46import java.util.ArrayList;
47import java.util.List;
48
49public class DexBackedMethodImplementation implements MethodImplementation {
50    @Nonnull public final DexFile dexFile;
51    private final int codeOffset;
52
53    public final int registerCount;
54    @Nonnull public final ImmutableList<? extends Instruction> instructions;
55    @Nonnull private final InstructionOffsetMap instructionOffsetMap;
56
57    // code_item offsets
58    private static final int TRIES_SIZE_OFFSET = 6;
59    private static final int INSTRUCTIONS_SIZE_OFFSET = 12;
60    private static final int INSTRUCTIONS_START_OFFSET = 16;
61
62    private static final int TRY_ITEM_SIZE = 8;
63
64    public DexBackedMethodImplementation(@Nonnull DexFile dexFile,
65                                         int codeOffset) {
66        this.dexFile = dexFile;
67        this.codeOffset = codeOffset;
68        this.registerCount = dexFile.readUshort(codeOffset);
69
70        instructions = buildInstructionList();
71        instructionOffsetMap = buildInstructionOffsetMap();
72    }
73
74    @Override public int getRegisterCount() { return registerCount; }
75    @Nonnull @Override public ImmutableList<? extends Instruction> getInstructions() { return instructions; }
76
77    @Nonnull
78    @Override
79    public List<? extends TryBlock> getTryBlocks() {
80        final int triesSize = dexFile.readUshort(codeOffset + TRIES_SIZE_OFFSET);
81        if (triesSize > 0) {
82            int instructionsSize = dexFile.readSmallUint(codeOffset + INSTRUCTIONS_SIZE_OFFSET);
83            final int triesStartOffset = AlignmentUtils.alignOffset(
84                    codeOffset + INSTRUCTIONS_START_OFFSET + (instructionsSize*2), 4);
85            final int handlersStartOffset = triesStartOffset + triesSize*TRY_ITEM_SIZE;
86
87            return new FixedSizeList<TryBlock>() {
88                @Override
89                public TryBlock readItem(int index) {
90                    return new DexBackedTryBlock(dexFile,
91                            triesStartOffset + index*TRY_ITEM_SIZE,
92                            handlersStartOffset,
93                            instructionOffsetMap);
94                }
95
96                @Override
97                public int size() {
98                    return triesSize;
99                }
100            };
101        }
102        return ImmutableList.of();
103    }
104
105    private ImmutableList<? extends Instruction> buildInstructionList() {
106        // instructionsSize is the number of 16-bit code units in the instruction list, not the number of instructions
107        int instructionsSize = dexFile.readSmallUint(codeOffset + INSTRUCTIONS_SIZE_OFFSET);
108
109        // we can use instructionsSize as an upper bound on the number of instructions there will be
110        ArrayList<Instruction> instructions = new ArrayList<Instruction>(instructionsSize);
111        int instructionsStartOffset = codeOffset + INSTRUCTIONS_START_OFFSET;
112        DexFileReader reader = dexFile.readerAt(instructionsStartOffset);
113        int endOffset = instructionsStartOffset + (instructionsSize*2);
114
115        while (reader.getOffset() < endOffset) {
116            instructions.add(DexBackedInstruction.readFrom(reader));
117        }
118
119        return ImmutableList.copyOf(instructions);
120    }
121
122    /**
123     * Builds an InstructionOffsetMap that maps an instruction offset to its index.
124     *
125     * This must be called after the instructions field has been set
126     *
127     * @return An InstructionOffsetMap object
128     */
129    private InstructionOffsetMap buildInstructionOffsetMap() {
130        int[] offsets = new int[instructions.size()];
131        int currentOffset = 0;
132        for (int i=0; i<offsets.length; i++) {
133            offsets[i] = currentOffset;
134            // TODO: need to handle variable size instructions
135            currentOffset += instructions.get(i).getOpcode().format.size;
136        }
137        return new InstructionOffsetMap(offsets);
138    }
139}
140