1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dexfuzz.rawdex;
18
19import dexfuzz.Log;
20
21public class Offset {
22  /**
23   * The absolute value of this offset as it was originally read.
24   */
25  private int originalOffset;
26
27  /**
28   * The Offsettable that this Offset points to.
29   */
30  private Offsettable offsettable;
31
32  /**
33   * The location of this Offset in the new file, ONLY SET IF the Offset
34   * couldn't be written because what it points to hasn't been written
35   * yet.
36   */
37  private int outputLocation;
38
39  /**
40   * Was the output location for this Offset set?.
41   */
42  private boolean outputLocationSet;
43
44  /**
45   * Does this Offset need to be written out using ULEB128?.
46   */
47  private boolean useUleb128;
48
49  /**
50   * Was this Offset created after reading, during mutation?.
51   */
52  private boolean isNewOffset;
53
54  /**
55   * Only one Offset should have this flag set, the MapItem that points
56   * to the HeaderItem.
57   */
58  private boolean pointsAtHeader;
59
60  /**
61   * If an Offset pointed at 0 (because it is not actually a valid Offset),
62   * and it's not pointing at the header, then this is set.
63   */
64  private boolean pointsAtNull;
65
66  public Offset(boolean header) {
67    pointsAtHeader = header;
68  }
69
70  public RawDexObject getPointedToItem() {
71    return offsettable.getItem();
72  }
73
74  public boolean pointsToSomething() {
75    return offsettable != null;
76  }
77
78  public boolean pointsAtNull() {
79    return pointsAtNull;
80  }
81
82  public boolean pointsAtHeader() {
83    return pointsAtHeader;
84  }
85
86  /**
87   * Returns true if this Offset points at the provided RawDexObject.
88   */
89  public boolean pointsToThisItem(RawDexObject thisItem) {
90    if (!pointsToSomething()) {
91      return false;
92    }
93    return (offsettable.getItem().equals(thisItem));
94  }
95
96  /**
97   * Returns true if this Offset points at the provided Offsettable.
98   */
99  public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
100    if (!pointsToSomething()) {
101      return false;
102    }
103    return (offsettable.equals(thisOffsettable));
104  }
105
106  /**
107   * Makes this Offset point at a new Offsettable.
108   */
109  public void pointTo(Offsettable offsettableItem) {
110    if (offsettable != null) {
111      Log.debug("Updating what an Offset points to...");
112    }
113    offsettable = offsettableItem;
114  }
115
116  /**
117   * Call this to make an Offset that pointed at null before now point at something.
118   * An Offset may have previously pointed at null before...
119   * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
120   * will point at null. We distinguish when Offsets point at null, and are not pointing
121   * at the header (only the header MapItem should do this) with a flag. Therefore, this
122   * method is needed to indicate that this Offset now points at something.
123   */
124  public void unsetNullAndPointTo(Offsettable offsettableItem) {
125    pointsAtNull = false;
126    if (offsettable != null) {
127      Log.debug("Updating what an Offset points to...");
128    }
129    offsettable = offsettableItem;
130  }
131
132  public void pointToNew(Offsettable offsettableItem) {
133    offsettable = offsettableItem;
134    isNewOffset = true;
135  }
136
137  public int getNewPositionOfItem() {
138    return offsettable.getNewPosition();
139  }
140
141  public boolean usesUleb128() {
142    return useUleb128;
143  }
144
145  /**
146   * Mark this Offset as using the ULEB128 encoding.
147   */
148  public void setUsesUleb128() {
149    if (useUleb128) {
150      throw new Error("Offset is already marked as using ULEB128!");
151    }
152    useUleb128 = true;
153  }
154
155  public boolean isNewOffset() {
156    return isNewOffset;
157  }
158
159  public void setPointsAtNull() {
160    pointsAtNull = true;
161  }
162
163  public void setOutputLocation(int loc) {
164    outputLocation = loc;
165    outputLocationSet = true;
166  }
167
168  /**
169   * Get the location in the output DEX file where this offset has been written.
170   * (This is used when patching Offsets when the Offsettable position was not
171   * known at the time of writing out the Offset.)
172   */
173  public int getOutputLocation() {
174    if (!outputLocationSet) {
175      throw new Error("Output location was not set yet!");
176    }
177    return outputLocation;
178  }
179
180  public void setOriginalOffset(int offset) {
181    originalOffset = offset;
182  }
183
184  public int getOriginalOffset() {
185    return originalOffset;
186  }
187
188  public boolean readyForWriting() {
189    return offsettable.readyForFinalOffsetToBeWritten();
190  }
191}
192