1959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle/*
2959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Copyright (C) 2014 The Android Open Source Project
3959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle *
4959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Licensed under the Apache License, Version 2.0 (the "License");
5959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * you may not use this file except in compliance with the License.
6959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * You may obtain a copy of the License at
7959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle *
8959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle *      http://www.apache.org/licenses/LICENSE-2.0
9959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle *
10959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Unless required by applicable law or agreed to in writing, software
11959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * distributed under the License is distributed on an "AS IS" BASIS,
12959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * See the License for the specific language governing permissions and
14959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * limitations under the License.
15959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */
16959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
17959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepackage dexfuzz.rawdex;
18959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
19959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.Log;
20959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
21959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.io.IOException;
22959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.ArrayList;
23959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.HashMap;
24959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.List;
25959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.Map;
26959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
27959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle/**
28959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * This class allows the recording of both Offsettable items (that is, items that can be
29959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * referred to by an offset somewhere else in the file - RawDexObjects) and Offsets.
30959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * The idea in a nutshell is that for every Offsettable item we read, we remember
31959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * its original position in the file using a map, and the order in which the Offsettables were
32959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * written out. We also remember every Offset we read in, and its value. Then, after reading
33959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * the whole file, we use the map to find the Offsettable it pointed at.
34959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Then, as we write out the file, for every Offsettable we write out, we record its new position,
35959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * using the order we collected earlier. For every Offset we write out, we look at its Offsettable
36959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * to see where it was written. If it hasn't been written yet, then we write out a blank value
37959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * for the time being, remember where that blank value was written, and put the Offset into a
38959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * table for patching once everything has been written out.
39959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * There are some variables (index_after_map_list, restore_point) used for remembering certain
40959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * points to jump forward and back to, because we cannot read and write the file out in exactly
41959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * the same order.
42959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * TODO: Perhaps it makes more sense to just reorder the offsettable_table once it's been read,
43959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * in preparation for the order in which the file is written out?
44959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Finally, we provide methods for adding new Offsettable items into the right place in the order
45959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * table.
46959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */
47959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepublic class OffsetTracker {
48959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
49959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * A map from the original offset in the input DEX file to
50959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the Offsettable it points to. (That Offsettable will contain
51959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the actual item, and later on the new offset for the item when
52959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the item is written out.
53959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
54959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private Map<Integer, Offsettable> offsettableMap;
55959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
56959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
57959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * A table of all Offsettables. We need to ensure we write out
58959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * all items in the same order we read them in, to make sure we update
59959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the Offsettable.new_position field with the correct value wrt to
60959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the original_position field.
61959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
62959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private List<Offsettable> offsettableTable;
63959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
64959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
65959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * A table of all offsets that is populated as we read in the DEX file.
66959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * As the end, we find the correct Offsettable for the Offset in the above
67959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * map, and associate them.
68959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
69959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private List<Offset> needsAssociationTable;
70959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
71959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
72959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * A table of all offsets that we could not write out an updated offset for
73959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * as we write out a DEX file. Will be checked after writing is complete,
74959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * to allow specific patching of each offset's location as at that point
75959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * all Offsettables will have been updated with their new position.
76959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
77959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private List<Offset> needsUpdateTable;
78959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
79959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
80959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Tracks how far we are through the offsettable_table as we write out the file.
81959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
82959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private int offsettableTableIdx;
83959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
84959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
85959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Because we write in a slightly different order to how we read
86959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * (why? to read, we read the header, then the map list, and then use the map
87959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   *  list to read everything else.
88959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   *  when we write, we write the header, and then we cannot write the map list
89959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   *  because we don't know where it will go yet, so we write everything else first)
90959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * so: we remember the position in the offsettable_table after we read the map list,
91959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * so we can skip there after we write out the header.
92959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
93959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private int indexAfterMapList;
94959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
95959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
96959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Related to index_after_map_list, this is the index we save when we're jumping back to
97959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * write the map list.
98959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
99959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private int restorePoint;
100959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
101959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
102959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Create a new OffsetTracker. Should persist between parsing a DEX file, and outputting
103959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the mutated DEX file.
104959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
105959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public OffsetTracker() {
106959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableMap = new HashMap<Integer,Offsettable>();
107959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTable = new ArrayList<Offsettable>();
108959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsAssociationTable = new ArrayList<Offset>();
109959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsUpdateTable = new ArrayList<Offset>();
110959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
111959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
112959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
113959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Lookup an Item by the offset it had in the input DEX file.
114959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param offset The offset in the input DEX file.
115959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @return The corresponding Item.
116959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
117959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public RawDexObject getItemByOffset(int offset) {
118959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return offsettableMap.get(offset).getItem();
119959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
120959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
121959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
122959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * As Items are read in, they call this function once they have word-aligned the file pointer,
123959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * to record their position and themselves into an Offsettable object, that will be tracked.
124959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param file Used for recording position into the new Offsettable.
125959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param item Used for recording the relevant Item into the new Offsettable.
126959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
127959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void getNewOffsettable(DexRandomAccessFile file, RawDexObject item) throws IOException {
128959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offsettable offsettable = new Offsettable(item, false);
129959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettable.setOriginalPosition((int) file.getFilePointer());
130959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableMap.put(offsettable.getOriginalPosition(), offsettable);
131959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTable.add(offsettable);
132959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
133959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
134959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
135959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * As Items read in Offsets, they call this function with the offset they originally
136959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * read from the file, to allow later association with an Offsettable.
137959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param originalOffset The original offset read from the input DEX file.
138959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @return An Offset that will later be associated with an Offsettable.
139959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
140959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offset getNewOffset(int originalOffset) throws IOException {
141959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offset offset = new Offset(false);
142959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offset.setOriginalOffset(originalOffset);
143959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsAssociationTable.add(offset);
144959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return offset;
145959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
146959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
147959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
148959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Only MapItem should call this method, when the MapItem that points to the header
149959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * is read.
150959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
151959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offset getNewHeaderOffset(int originalOffset) throws IOException {
152959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offset offset = new Offset(true);
153959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offset.setOriginalOffset(originalOffset);
154959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsAssociationTable.add(offset);
155959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return offset;
156959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
157959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
158959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
159959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Call this after reading, to associate Offsets with Offsettables.
160959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
161959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void associateOffsets() {
162959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (Offset offset : needsAssociationTable) {
163959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offset.getOriginalOffset() == 0 && !(offset.pointsAtHeader())) {
164959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        offset.setPointsAtNull();
165959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      } else {
166959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        offset.pointTo(offsettableMap.get(offset.getOriginalOffset()));
167959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        if (!offset.pointsToSomething()) {
168959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          Log.error(String.format("Couldn't find original offset 0x%x!",
169959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle              offset.getOriginalOffset()));
170959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        }
171959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
172959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
173959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsAssociationTable.clear();
174959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
175959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
176959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
177959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * As Items are written out into the output DEX file, this function is called
178959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * to update the next Offsettable with the file pointer's current position.
179959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * This should allow the tracking of new offset locations.
180959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * This also requires that reading and writing of all items happens in the same order
181959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * (with the exception of the map list, see above)
182959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param file Used for recording the new position.
183959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
184959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void updatePositionOfNextOffsettable(DexRandomAccessFile file) throws IOException {
185959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (offsettableTableIdx == offsettableTable.size()) {
186959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Not all created Offsettable items have been added to the "
187959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          + "Offsettable Table!");
188959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
189959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offsettable offsettable = offsettableTable.get(offsettableTableIdx);
190959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettable.setNewPosition((int) file.getFilePointer());
191959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTableIdx++;
192959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
193959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
194959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
195959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * As Items are written out, any writing out of an offset must call this function, passing
196959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * in the relevant offset. This function will write out the offset, if the associated
197959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Offsettable has been updated with its new position, or else will write out a null value, and
198959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * the Offset will be stored for writing after all Items have been written, and all
199959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Offsettables MUST have been updated.
200959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param offset The offset received from getNewOffset().
201959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param file Used for writing out to the file.
202959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param useUleb128 Whether or not the offset should be written in UINT or ULEB128 form.
203959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
204959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void tryToWriteOffset(Offset offset, DexRandomAccessFile file, boolean useUleb128)
205959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      throws IOException {
206959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (!offset.isNewOffset() && (!offset.pointsToSomething())) {
207959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (useUleb128) {
208959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUleb128(0);
209959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      } else {
210959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUInt(0);
211959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
212959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      return;
213959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
214959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
215959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (offset.readyForWriting()) {
216959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (useUleb128) {
217959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUleb128(offset.getNewPositionOfItem());
218959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      } else {
219959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUInt(offset.getNewPositionOfItem());
220959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
221959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else {
222959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      offset.setOutputLocation((int) file.getFilePointer());
223959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (useUleb128) {
224959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeLargestUleb128(offset.getOriginalOffset());
225959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        offset.setUsesUleb128();
226959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      } else {
227959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUInt(offset.getOriginalOffset());
228959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
229959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      needsUpdateTable.add(offset);
230959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
231959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
232959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
233959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
234959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * This is called after all writing has finished, to write out any Offsets
235959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * that could not be written out during the original writing phase, because their
236959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * associated Offsettables hadn't had their new positions calculated yet.
237959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * @param file Used for writing out to the file.
238959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
239959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void updateOffsets(DexRandomAccessFile file) throws IOException {
240959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (offsettableTableIdx != offsettableTable.size()) {
241959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Being asked to update dangling offsets but the "
242959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          + "correct number of offsettables has not been written out!");
243959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
244959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (Offset offset : needsUpdateTable) {
245959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      file.seek(offset.getOutputLocation());
246959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offset.usesUleb128()) {
247959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeLargestUleb128(offset.getNewPositionOfItem());
248959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      } else {
249959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        file.writeUInt(offset.getNewPositionOfItem());
250959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
251959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
252959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    needsUpdateTable.clear();
253959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
254959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
255959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
256959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Called after writing out the header, to skip to after the map list.
257959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
258959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void skipToAfterMapList() {
259959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTableIdx = indexAfterMapList;
260959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
261959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
262959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
263959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Called once the map list needs to be written out, to set the
264959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * offsettable table index back to the right place.
265959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
266959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void goBackToMapList() {
267959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    restorePoint = offsettableTableIdx;
268959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTableIdx = (indexAfterMapList - 1);
269959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
270959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
271959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
272959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Called once the map list has been written out, to set the
273959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * offsettable table index back to where it was before.
274959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
275959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void goBackToPreviousPoint() {
276959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (offsettableTableIdx != indexAfterMapList) {
277959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Being asked to go to the point before the MapList was written out,"
278959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          + " but we're not in the right place.");
279959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
280959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTableIdx = restorePoint;
281959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
282959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
283959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
284959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Called after reading in the map list, to remember the point to be skipped
285959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * to later.
286959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
287959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public void rememberPointAfterMapList() {
288959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    indexAfterMapList = offsettableTable.size();
289959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
290959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
291959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private void updateHeaderOffsetIfValid(Offset offset, Offsettable previousFirst,
292959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Offsettable newFirst, String offsetName) {
293959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (offset.pointsToThisOffsettable(previousFirst)) {
294959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      offset.pointToNew(newFirst);
295959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else {
296959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Header " + offsetName + " offset not pointing at first element?");
297959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
298959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
299959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
300959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private void addTypeListsToMapFile(RawDexFile rawDexFile, Offsettable typeListOffsettable) {
301959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Create a MapItem for the TypeLists
302959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    MapItem typeListMapItem = new MapItem();
303959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    typeListMapItem.offset = new Offset(false);
304959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    typeListMapItem.offset.pointToNew(typeListOffsettable);
305959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    typeListMapItem.type = MapItem.TYPE_TYPE_LIST;
306959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    typeListMapItem.size = 1;
307959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
308959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Insert into the MapList.
309959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // (first, find the MapItem that points to StringDataItems...)
310959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    int idx = 0;
311959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
312959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (mapItem.type == MapItem.TYPE_STRING_DATA_ITEM) {
313959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        break;
314959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
315959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      idx++;
316959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
317959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // (now insert the TypeList MapItem just before the StringDataItem one...)
318959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    rawDexFile.mapList.mapItems.add(idx, typeListMapItem);
319959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
320959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
321959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private void addFieldIdsToHeaderAndMapFile(RawDexFile rawDexFile,
322959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Offsettable fieldOffsettable) {
323959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Add the field IDs to the header.
324959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    rawDexFile.header.fieldIdsOff.unsetNullAndPointTo(fieldOffsettable);
325959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    rawDexFile.header.fieldIdsSize = 1;
326959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
327959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Create a MapItem for the field IDs.
328959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    MapItem fieldMapItem = new MapItem();
329959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    fieldMapItem.offset = new Offset(false);
330959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    fieldMapItem.offset.pointToNew(fieldOffsettable);
331959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    fieldMapItem.type = MapItem.TYPE_FIELD_ID_ITEM;
332959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    fieldMapItem.size = 1;
333959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
334959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Insert into the MapList.
335959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // (first, find the MapItem that points to MethodIdItems...)
336959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    int idx = 0;
337959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
338959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (mapItem.type == MapItem.TYPE_METHOD_ID_ITEM) {
339959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        break;
340959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
341959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      idx++;
342959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
343959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // (now insert the FieldIdItem MapItem just before the MethodIdItem one...)
344959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    rawDexFile.mapList.mapItems.add(idx, fieldMapItem);
345959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
346959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
347959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
348959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private void updateOffsetsInHeaderAndMapFile(RawDexFile rawDexFile,
349959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Offsettable newFirstOffsettable) {
350959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offsettable prevFirstOffsettable = null;
351959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
352959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i) == newFirstOffsettable) {
353959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        prevFirstOffsettable = offsettableTable.get(i + 1);
354959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        break;
355959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
356959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
357959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (prevFirstOffsettable == null) {
358959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("When calling updateMapListOffsets, could not find new "
359959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          + "first offsettable?");
360959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
361959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
362959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Based on the type of the item we just added, check the relevant Offset in the header
363959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // and if it pointed at the prev_first_offsettable, make it point at the new one.
364959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // NB: if it isn't pointing at the prev one, something is wrong.
365959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    HeaderItem header = rawDexFile.header;
366959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (newFirstOffsettable.getItem() instanceof StringIdItem) {
367959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.stringIdsOff, prevFirstOffsettable,
368959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "StringID");
369959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else if (newFirstOffsettable.getItem() instanceof TypeIdItem) {
370959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.typeIdsOff, prevFirstOffsettable,
371959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "TypeID");
372959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else if (newFirstOffsettable.getItem() instanceof ProtoIdItem) {
373959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.protoIdsOff, prevFirstOffsettable,
374959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "ProtoID");
375959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else if (newFirstOffsettable.getItem() instanceof FieldIdItem) {
376959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.fieldIdsOff, prevFirstOffsettable,
377959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "FieldID");
378959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else if (newFirstOffsettable.getItem() instanceof MethodIdItem) {
379959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.methodIdsOff, prevFirstOffsettable,
380959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "MethodID");
381959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    } else if (newFirstOffsettable.getItem() instanceof ClassDefItem) {
382959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      updateHeaderOffsetIfValid(header.classDefsOff, prevFirstOffsettable,
383959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle          newFirstOffsettable, "ClassDef");
384959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
385959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
386959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // Now iterate through the MapList's MapItems, and see if their Offsets pointed at the
387959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // prev_first_offsettable, and if so, make them now point at the new_first_offsettable.
388959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
389959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (mapItem.offset.pointsToThisOffsettable(prevFirstOffsettable)) {
390959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        Log.info("Updating offset in MapItem (type: " + mapItem.type + ") after "
391959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle            + "we called insertNewOffsettableAsFirstOfType()");
392959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        mapItem.offset.pointToNew(newFirstOffsettable);
393959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
394959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
395959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
396959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
397959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private void insertOffsettableAt(int idx, Offsettable offsettable) {
398959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    offsettableTable.add(idx, offsettable);
399959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (indexAfterMapList > idx) {
400959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      indexAfterMapList++;
401959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
402959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (restorePoint > idx) {
403959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      restorePoint++;
404959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
405959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
406959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
407959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
408959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * If we're creating our first TypeList, then IdCreator has to call this method to
409959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * ensure it gets put into the correct place in the offsettable table.
410959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * This assumes TypeLists always come before StringDatas.
411959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
412959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offsettable insertNewOffsettableAsFirstEverTypeList(RawDexObject item,
413959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      RawDexFile rawDexFile) {
414959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // We find the first StringDataItem, the type lists will come before this.
415959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.info("Calling insertNewOffsettableAsFirstEverTypeList()");
416959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
417959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i).getItem() instanceof StringDataItem) {
418959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        Offsettable offsettable = new Offsettable(item, true);
419959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        insertOffsettableAt(i, offsettable);
420959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        addTypeListsToMapFile(rawDexFile, offsettable);
421959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        return offsettable;
422959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
423959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
424959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.errorAndQuit("Could not find any StringDataItems to insert the type list before.");
425959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return null;
426959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
427959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
428959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
429959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * If we're creating our first FieldId, then IdCreator has to call this method to
430959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * ensure it gets put into the correct place in the offsettable table.
431959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * This assumes FieldIds always come before MethodIds.
432959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
433959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offsettable insertNewOffsettableAsFirstEverField(RawDexObject item,
434959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      RawDexFile rawDexFile) {
435959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    // We find the first MethodIdItem, the fields will come before this.
436959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.info("Calling insertNewOffsettableAsFirstEverField()");
437959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
438959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i).getItem() instanceof MethodIdItem) {
439959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        Offsettable offsettable = new Offsettable(item, true);
440959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        insertOffsettableAt(i, offsettable);
441959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        addFieldIdsToHeaderAndMapFile(rawDexFile, offsettable);
442959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        return offsettable;
443959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
444959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
445959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.errorAndQuit("Could not find any MethodIdItems to insert the field before.");
446959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return null;
447959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
448959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
449959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
450959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * If we're creating a new Item (such as FieldId, MethodId) that is placed into the
451959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * first position of the relevant ID table, then IdCreator has to call this method to
452959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * ensure it gets put into the correct place in the offsettable table.
453959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
454959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offsettable insertNewOffsettableAsFirstOfType(RawDexObject item,
455959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      RawDexFile rawDexFile) {
456959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.debug("Calling insertNewOffsettableAsFirstOfType()");
457959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    int index = getOffsettableIndexForFirstItemType(item);
458959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (index == -1) {
459959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Could not find any object of class: " + item.getClass());
460959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
461959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offsettable offsettable = new Offsettable(item, true);
462959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    insertOffsettableAt(index, offsettable);
463959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    updateOffsetsInHeaderAndMapFile(rawDexFile, offsettable);
464959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return offsettable;
465959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
466959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
467959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
468959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * IdCreator has to call this method when it creates a new IdItem, to make sure it
469959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * gets put into the correct place in the offsettable table. IdCreator should
470959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * provide the IdItem that should come before this new IdItem.
471959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
472959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offsettable insertNewOffsettableAfter(RawDexObject item, RawDexObject itemBefore) {
473959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Log.debug("Calling insertNewOffsettableAfter()");
474959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    int index = getOffsettableIndexForItem(itemBefore);
475959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    if (index == -1) {
476959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      Log.errorAndQuit("Did not find specified 'after' object in offsettable table.");
477959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
478959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Offsettable offsettable = new Offsettable(item, true);
479959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    insertOffsettableAt(index + 1, offsettable);
480959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return offsettable;
481959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
482959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
483959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private int getOffsettableIndexForFirstItemType(RawDexObject item) {
484959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    Class<?> itemClass = item.getClass();
485959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
486959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i).getItem().getClass().equals(itemClass)) {
487959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        return i;
488959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
489959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
490959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return -1;
491959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
492959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
493959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  private int getOffsettableIndexForItem(RawDexObject item) {
494959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
495959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i).getItem() == item) {
496959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        return i;
497959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
498959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
499959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return -1;
500959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
501959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle
502959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  /**
503959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   * Given a RawDexObject, get the Offsettable that contains it.
504959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle   */
505959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  public Offsettable getOffsettableForItem(RawDexObject item) {
506959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    for (int i = 0; i < offsettableTable.size(); i++) {
507959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      if (offsettableTable.get(i).getItem() == item) {
508959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle        return offsettableTable.get(i);
509959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle      }
510959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    }
511959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle    return null;
512959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle  }
513959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle}
514