Home | History | Annotate | Download | only in file
      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.dexgen.dex.file;
     18 
     19 import com.android.dexgen.util.AnnotatedOutput;
     20 import com.android.dexgen.util.ExceptionWithContext;
     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      */
     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      */
     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
     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     public final int compareTo(OffsettedItem other) {
    115         if (this == other) {
    116             return 0;
    117         }
    118 
    119         ItemType thisType = itemType();
    120         ItemType otherType = other.itemType();
    121 
    122         if (thisType != otherType) {
    123             return thisType.compareTo(otherType);
    124         }
    125 
    126         return compareTo0(other);
    127     }
    128 
    129     /**
    130      * Sets the write size of this item. This may only be called once
    131      * per instance, and only if the size was unknown upon instance
    132      * creation.
    133      *
    134      * @param writeSize {@code > 0;} the write size, in bytes
    135      */
    136     public final void setWriteSize(int writeSize) {
    137         if (writeSize < 0) {
    138             throw new IllegalArgumentException("writeSize < 0");
    139         }
    140 
    141         if (this.writeSize >= 0) {
    142             throw new UnsupportedOperationException("writeSize already set");
    143         }
    144 
    145         this.writeSize = writeSize;
    146     }
    147 
    148     /** {@inheritDoc}
    149      *
    150      * @throws UnsupportedOperationException thrown if the write size
    151      * is not yet known
    152      */
    153     @Override
    154     public final int writeSize() {
    155         if (writeSize < 0) {
    156             throw new UnsupportedOperationException("writeSize is unknown");
    157         }
    158 
    159         return writeSize;
    160     }
    161 
    162     /** {@inheritDoc} */
    163     @Override
    164     public final void writeTo(DexFile file, AnnotatedOutput out) {
    165         out.alignTo(alignment);
    166 
    167         try {
    168             if (writeSize < 0) {
    169                 throw new UnsupportedOperationException(
    170                         "writeSize is unknown");
    171             }
    172             out.assertCursor(getAbsoluteOffset());
    173         } catch (RuntimeException ex) {
    174             throw ExceptionWithContext.withContext(ex,
    175                     "...while writing " + this);
    176         }
    177 
    178         writeTo0(file, out);
    179     }
    180 
    181     /**
    182      * Gets the relative item offset. The offset is from the start of
    183      * the section which the instance was written to.
    184      *
    185      * @return {@code >= 0;} the offset
    186      * @throws RuntimeException thrown if the offset is not yet known
    187      */
    188     public final int getRelativeOffset() {
    189         if (offset < 0) {
    190             throw new RuntimeException("offset not yet known");
    191         }
    192 
    193         return offset;
    194     }
    195 
    196     /**
    197      * Gets the absolute item offset. The offset is from the start of
    198      * the file which the instance was written to.
    199      *
    200      * @return {@code >= 0;} the offset
    201      * @throws RuntimeException thrown if the offset is not yet known
    202      */
    203     public final int getAbsoluteOffset() {
    204         if (offset < 0) {
    205             throw new RuntimeException("offset not yet known");
    206         }
    207 
    208         return addedTo.getAbsoluteOffset(offset);
    209     }
    210 
    211     /**
    212      * Indicates that this item has been added to the given section at
    213      * the given offset. It is only valid to call this method once per
    214      * instance.
    215      *
    216      * @param addedTo {@code non-null;} the section this instance has
    217      * been added to
    218      * @param offset {@code >= 0;} the desired offset from the start of the
    219      * section where this instance was placed
    220      * @return {@code >= 0;} the offset that this instance should be placed at
    221      * in order to meet its alignment constraint
    222      */
    223     public final int place(Section addedTo, int offset) {
    224         if (addedTo == null) {
    225             throw new NullPointerException("addedTo == null");
    226         }
    227 
    228         if (offset < 0) {
    229             throw new IllegalArgumentException("offset < 0");
    230         }
    231 
    232         if (this.addedTo != null) {
    233             throw new RuntimeException("already written");
    234         }
    235 
    236         int mask = alignment - 1;
    237         offset = (offset + mask) & ~mask;
    238 
    239         this.addedTo = addedTo;
    240         this.offset = offset;
    241 
    242         place0(addedTo, offset);
    243 
    244         return offset;
    245     }
    246 
    247     /**
    248      * Gets the alignment requirement of this instance. An instance should
    249      * only be written when so aligned.
    250      *
    251      * @return {@code > 0;} the alignment requirement; must be a power of 2
    252      */
    253     public final int getAlignment() {
    254         return alignment;
    255     }
    256 
    257     /**
    258      * Gets the absolute offset of this item as a string, suitable for
    259      * including in annotations.
    260      *
    261      * @return {@code non-null;} the offset string
    262      */
    263     public final String offsetString() {
    264         return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
    265     }
    266 
    267     /**
    268      * Gets a short human-readable string representing this instance.
    269      *
    270      * @return {@code non-null;} the human form
    271      */
    272     public abstract String toHuman();
    273 
    274     /**
    275      * Compares this instance to another which is guaranteed to be of
    276      * the same class. The default implementation of this method is to
    277      * throw an exception (unsupported operation). If a particular
    278      * class needs to actually sort, then it should override this
    279      * method.
    280      *
    281      * @param other {@code non-null;} instance to compare to
    282      * @return {@code -1}, {@code 0}, or {@code 1}, depending
    283      * on the sort order of this instance and the other
    284      */
    285     protected int compareTo0(OffsettedItem other) {
    286         throw new UnsupportedOperationException("unsupported");
    287     }
    288 
    289     /**
    290      * Does additional work required when placing an instance. The
    291      * default implementation of this method is a no-op. If a
    292      * particular class needs to do something special, then it should
    293      * override this method. In particular, if this instance did not
    294      * know its write size up-front, then this method is responsible
    295      * for setting it.
    296      *
    297      * @param addedTo {@code non-null;} the section this instance has been added to
    298      * @param offset {@code >= 0;} the offset from the start of the
    299      * section where this instance was placed
    300      */
    301     protected void place0(Section addedTo, int offset) {
    302         // This space intentionally left blank.
    303     }
    304 
    305     /**
    306      * Performs the actual write of the contents of this instance to
    307      * the given data section. This is called by {@link #writeTo},
    308      * which will have taken care of ensuring alignment.
    309      *
    310      * @param file {@code non-null;} the file to use for reference
    311      * @param out {@code non-null;} where to write to
    312      */
    313     protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
    314 }
    315