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