• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 
17 package com.android.dx.dex.file;
18 
19 import com.android.dex.util.ExceptionWithContext;
20 import com.android.dx.util.AnnotatedOutput;
21 
22 /**
23  * An item in a Dalvik file which is referenced by absolute offset.
24  */
25 public abstract class OffsettedItem extends Item
26         implements Comparable<OffsettedItem> {
27     /** {@code > 0;} alignment requirement */
28     private final int alignment;
29 
30     /** {@code >= -1;} the size of this instance when written, in bytes, or
31      * {@code -1} if not yet known */
32     private int writeSize;
33 
34     /**
35      * {@code null-ok;} section the item was added to, or {@code null} if
36      * not yet added
37      */
38     private Section addedTo;
39 
40     /**
41      * {@code >= -1;} assigned offset of the item from the start of its section,
42      * or {@code -1} if not yet assigned
43      */
44     private int offset;
45 
46     /**
47      * Gets the absolute offset of the given item, returning {@code 0}
48      * if handed {@code null}.
49      *
50      * @param item {@code null-ok;} the item in question
51      * @return {@code >= 0;} the item's absolute offset, or {@code 0}
52      * if {@code item == null}
53      */
getAbsoluteOffsetOr0(OffsettedItem item)54     public static int getAbsoluteOffsetOr0(OffsettedItem item) {
55         if (item == null) {
56             return 0;
57         }
58 
59         return item.getAbsoluteOffset();
60     }
61 
62     /**
63      * Constructs an instance. The offset is initially unassigned.
64      *
65      * @param alignment {@code > 0;} output alignment requirement; must be a
66      * power of 2
67      * @param writeSize {@code >= -1;} the size of this instance when written,
68      * in bytes, or {@code -1} if not immediately known
69      */
OffsettedItem(int alignment, int writeSize)70     public OffsettedItem(int alignment, int writeSize) {
71         Section.validateAlignment(alignment);
72 
73         if (writeSize < -1) {
74             throw new IllegalArgumentException("writeSize < -1");
75         }
76 
77         this.alignment = alignment;
78         this.writeSize = writeSize;
79         this.addedTo = null;
80         this.offset = -1;
81     }
82 
83     /**
84      * {@inheritDoc}
85      *
86      * Comparisons for this class are defined to be type-major (if the
87      * types don't match then the objects are not equal), with
88      * {@link #compareTo0} deciding same-type comparisons.
89      */
90     @Override
equals(Object other)91     public final boolean equals(Object other) {
92         if (this == other) {
93             return true;
94         }
95 
96         OffsettedItem otherItem = (OffsettedItem) other;
97         ItemType thisType = itemType();
98         ItemType otherType = otherItem.itemType();
99 
100         if (thisType != otherType) {
101             return false;
102         }
103 
104         return (compareTo0(otherItem) == 0);
105     }
106 
107     /**
108      * {@inheritDoc}
109      *
110      * Comparisons for this class are defined to be class-major (if the
111      * classes don't match then the objects are not equal), with
112      * {@link #compareTo0} deciding same-class comparisons.
113      */
114     @Override
compareTo(OffsettedItem other)115     public final int compareTo(OffsettedItem other) {
116         if (this == other) {
117             return 0;
118         }
119 
120         ItemType thisType = itemType();
121         ItemType otherType = other.itemType();
122 
123         if (thisType != otherType) {
124             return thisType.compareTo(otherType);
125         }
126 
127         return compareTo0(other);
128     }
129 
130     /**
131      * Sets the write size of this item. This may only be called once
132      * per instance, and only if the size was unknown upon instance
133      * creation.
134      *
135      * @param writeSize {@code > 0;} the write size, in bytes
136      */
setWriteSize(int writeSize)137     public final void setWriteSize(int writeSize) {
138         if (writeSize < 0) {
139             throw new IllegalArgumentException("writeSize < 0");
140         }
141 
142         if (this.writeSize >= 0) {
143             throw new UnsupportedOperationException("writeSize already set");
144         }
145 
146         this.writeSize = writeSize;
147     }
148 
149     /** {@inheritDoc}
150      *
151      * @throws UnsupportedOperationException thrown if the write size
152      * is not yet known
153      */
154     @Override
writeSize()155     public final int writeSize() {
156         if (writeSize < 0) {
157             throw new UnsupportedOperationException("writeSize is unknown");
158         }
159 
160         return writeSize;
161     }
162 
163     /** {@inheritDoc} */
164     @Override
writeTo(DexFile file, AnnotatedOutput out)165     public final void writeTo(DexFile file, AnnotatedOutput out) {
166         out.alignTo(alignment);
167 
168         try {
169             if (writeSize < 0) {
170                 throw new UnsupportedOperationException(
171                         "writeSize is unknown");
172             }
173             out.assertCursor(getAbsoluteOffset());
174         } catch (RuntimeException ex) {
175             throw ExceptionWithContext.withContext(ex,
176                     "...while writing " + this);
177         }
178 
179         writeTo0(file, out);
180     }
181 
182     /**
183      * Gets the relative item offset. The offset is from the start of
184      * the section which the instance was written to.
185      *
186      * @return {@code >= 0;} the offset
187      * @throws RuntimeException thrown if the offset is not yet known
188      */
getRelativeOffset()189     public final int getRelativeOffset() {
190         if (offset < 0) {
191             throw new RuntimeException("offset not yet known");
192         }
193 
194         return offset;
195     }
196 
197     /**
198      * Gets the absolute item offset. The offset is from the start of
199      * the file which the instance was written to.
200      *
201      * @return {@code >= 0;} the offset
202      * @throws RuntimeException thrown if the offset is not yet known
203      */
getAbsoluteOffset()204     public final int getAbsoluteOffset() {
205         if (offset < 0) {
206             throw new RuntimeException("offset not yet known");
207         }
208 
209         return addedTo.getAbsoluteOffset(offset);
210     }
211 
212     /**
213      * Indicates that this item has been added to the given section at
214      * the given offset. It is only valid to call this method once per
215      * instance.
216      *
217      * @param addedTo {@code non-null;} the section this instance has
218      * been added to
219      * @param offset {@code >= 0;} the desired offset from the start of the
220      * section where this instance was placed
221      * @return {@code >= 0;} the offset that this instance should be placed at
222      * in order to meet its alignment constraint
223      */
place(Section addedTo, int offset)224     public final int place(Section addedTo, int offset) {
225         if (addedTo == null) {
226             throw new NullPointerException("addedTo == null");
227         }
228 
229         if (offset < 0) {
230             throw new IllegalArgumentException("offset < 0");
231         }
232 
233         if (this.addedTo != null) {
234             throw new RuntimeException("already written");
235         }
236 
237         int mask = alignment - 1;
238         offset = (offset + mask) & ~mask;
239 
240         this.addedTo = addedTo;
241         this.offset = offset;
242 
243         place0(addedTo, offset);
244 
245         return offset;
246     }
247 
248     /**
249      * Gets the alignment requirement of this instance. An instance should
250      * only be written when so aligned.
251      *
252      * @return {@code > 0;} the alignment requirement; must be a power of 2
253      */
getAlignment()254     public final int getAlignment() {
255         return alignment;
256     }
257 
258     /**
259      * Gets the absolute offset of this item as a string, suitable for
260      * including in annotations.
261      *
262      * @return {@code non-null;} the offset string
263      */
offsetString()264     public final String offsetString() {
265         return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
266     }
267 
268     /**
269      * Gets a short human-readable string representing this instance.
270      *
271      * @return {@code non-null;} the human form
272      */
toHuman()273     public abstract String toHuman();
274 
275     /**
276      * Compares this instance to another which is guaranteed to be of
277      * the same class. The default implementation of this method is to
278      * throw an exception (unsupported operation). If a particular
279      * class needs to actually sort, then it should override this
280      * method.
281      *
282      * @param other {@code non-null;} instance to compare to
283      * @return {@code -1}, {@code 0}, or {@code 1}, depending
284      * on the sort order of this instance and the other
285      */
compareTo0(OffsettedItem other)286     protected int compareTo0(OffsettedItem other) {
287         throw new UnsupportedOperationException("unsupported");
288     }
289 
290     /**
291      * Does additional work required when placing an instance. The
292      * default implementation of this method is a no-op. If a
293      * particular class needs to do something special, then it should
294      * override this method. In particular, if this instance did not
295      * know its write size up-front, then this method is responsible
296      * for setting it.
297      *
298      * @param addedTo {@code non-null;} the section this instance has been added to
299      * @param offset {@code >= 0;} the offset from the start of the
300      * section where this instance was placed
301      */
place0(Section addedTo, int offset)302     protected void place0(Section addedTo, int offset) {
303         // This space intentionally left blank.
304     }
305 
306     /**
307      * Performs the actual write of the contents of this instance to
308      * the given data section. This is called by {@link #writeTo},
309      * which will have taken care of ensuring alignment.
310      *
311      * @param file {@code non-null;} the file to use for reference
312      * @param out {@code non-null;} where to write to
313      */
writeTo0(DexFile file, AnnotatedOutput out)314     protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
315 }
316