diff options
Diffstat (limited to 'dx/src/com/android/dx/dex/file/MixedItemSection.java')
-rw-r--r-- | dx/src/com/android/dx/dex/file/MixedItemSection.java | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/dex/file/MixedItemSection.java b/dx/src/com/android/dx/dex/file/MixedItemSection.java new file mode 100644 index 0000000..b885306 --- /dev/null +++ b/dx/src/com/android/dx/dex/file/MixedItemSection.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dx.dex.file; + +import com.android.dx.util.AnnotatedOutput; +import com.android.dx.util.ExceptionWithContext; +import com.android.dx.util.Hex; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.TreeMap; + +/** + * A section of a {@code .dex} file which consists of a sequence of + * {@link OffsettedItem} objects, which may each be of a different concrete + * class and/or size. + * + * <b>Note:</b> It is invalid for an item in an instance of this class to + * have a larger alignment requirement than the alignment of this instance. + */ +public final class MixedItemSection extends Section { + static enum SortType { + /** no sorting */ + NONE, + + /** sort by type only */ + TYPE, + + /** sort in class-major order, with instances sorted per-class */ + INSTANCE; + }; + + /** {@code non-null;} sorter which sorts instances by type */ + private static final Comparator<OffsettedItem> TYPE_SORTER = + new Comparator<OffsettedItem>() { + public int compare(OffsettedItem item1, OffsettedItem item2) { + ItemType type1 = item1.itemType(); + ItemType type2 = item2.itemType(); + return type1.compareTo(type2); + } + }; + + /** {@code non-null;} the items in this part */ + private final ArrayList<OffsettedItem> items; + + /** {@code non-null;} items that have been explicitly interned */ + private final HashMap<OffsettedItem, OffsettedItem> interns; + + /** {@code non-null;} how to sort the items */ + private final SortType sort; + + /** + * {@code >= -1;} the current size of this part, in bytes, or {@code -1} + * if not yet calculated + */ + private int writeSize; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param name {@code null-ok;} the name of this instance, for annotation + * purposes + * @param file {@code non-null;} file that this instance is part of + * @param alignment {@code > 0;} alignment requirement for the final output; + * must be a power of 2 + * @param sort how the items should be sorted in the final output + */ + public MixedItemSection(String name, DexFile file, int alignment, + SortType sort) { + super(name, file, alignment); + + this.items = new ArrayList<OffsettedItem>(100); + this.interns = new HashMap<OffsettedItem, OffsettedItem>(100); + this.sort = sort; + this.writeSize = -1; + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return items; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + throwIfNotPrepared(); + return writeSize; + } + + /** {@inheritDoc} */ + @Override + public int getAbsoluteItemOffset(Item item) { + OffsettedItem oi = (OffsettedItem) item; + return oi.getAbsoluteOffset(); + } + + /** + * Gets the size of this instance, in items. + * + * @return {@code >= 0;} the size + */ + public int size() { + return items.size(); + } + + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); + + if (writeSize == -1) { + throw new RuntimeException("write size not yet set"); + } + + int sz = writeSize; + int offset = (sz == 0) ? 0 : getFileOffset(); + String name = getName(); + + if (name == null) { + name = "<unnamed>"; + } + + int spaceCount = 15 - name.length(); + char[] spaceArr = new char[spaceCount]; + Arrays.fill(spaceArr, ' '); + String spaces = new String(spaceArr); + + if (out.annotates()) { + out.annotate(4, name + "_size:" + spaces + Hex.u4(sz)); + out.annotate(4, name + "_off: " + spaces + Hex.u4(offset)); + } + + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Adds an item to this instance. This will in turn tell the given item + * that it has been added to this instance. It is invalid to add the + * same item to more than one instance, nor to add the same items + * multiple times to a single instance. + * + * @param item {@code non-null;} the item to add + */ + public void add(OffsettedItem item) { + throwIfPrepared(); + + try { + if (item.getAlignment() > getAlignment()) { + throw new IllegalArgumentException( + "incompatible item alignment"); + } + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("item == null"); + } + + items.add(item); + } + + /** + * Interns an item in this instance, returning the interned instance + * (which may not be the one passed in). This will add the item if no + * equal item has been added. + * + * @param item {@code non-null;} the item to intern + * @return {@code non-null;} the equivalent interned instance + */ + public <T extends OffsettedItem> T intern(T item) { + throwIfPrepared(); + + OffsettedItem result = interns.get(item); + + if (result != null) { + return (T) result; + } + + add(item); + interns.put(item, item); + return item; + } + + /** + * Gets an item which was previously interned. + * + * @param item {@code non-null;} the item to look for + * @return {@code non-null;} the equivalent already-interned instance + */ + public <T extends OffsettedItem> T get(T item) { + throwIfNotPrepared(); + + OffsettedItem result = interns.get(item); + + if (result != null) { + return (T) result; + } + + throw new NoSuchElementException(item.toString()); + } + + /** + * Writes an index of contents of the items in this instance of the + * given type. If there are none, this writes nothing. If there are any, + * then the index is preceded by the given intro string. + * + * @param out {@code non-null;} where to write to + * @param itemType {@code non-null;} the item type of interest + * @param intro {@code non-null;} the introductory string for non-empty indices + */ + public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType, + String intro) { + throwIfNotPrepared(); + + TreeMap<String, OffsettedItem> index = + new TreeMap<String, OffsettedItem>(); + + for (OffsettedItem item : items) { + if (item.itemType() == itemType) { + String label = item.toHuman(); + index.put(label, item); + } + } + + if (index.size() == 0) { + return; + } + + out.annotate(0, intro); + + for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) { + String label = entry.getKey(); + OffsettedItem item = entry.getValue(); + out.annotate(0, item.offsetString() + ' ' + label + '\n'); + } + } + + /** {@inheritDoc} */ + @Override + protected void prepare0() { + DexFile file = getFile(); + + /* + * It's okay for new items to be added as a result of an + * addContents() call; we just have to deal with the possibility. + */ + + int i = 0; + for (;;) { + int sz = items.size(); + if (i >= sz) { + break; + } + + for (/*i*/; i < sz; i++) { + OffsettedItem one = items.get(i); + one.addContents(file); + } + } + } + + /** + * Places all the items in this instance at particular offsets. This + * will call {@link OffsettedItem#place} on each item. If an item + * does not know its write size before the call to {@code place}, + * it is that call which is responsible for setting the write size. + * This method may only be called once per instance; subsequent calls + * will throw an exception. + */ + public void placeItems() { + throwIfNotPrepared(); + + switch (sort) { + case INSTANCE: { + Collections.sort(items); + break; + } + case TYPE: { + Collections.sort(items, TYPE_SORTER); + break; + } + } + + int sz = items.size(); + int outAt = 0; + for (int i = 0; i < sz; i++) { + OffsettedItem one = items.get(i); + try { + int placedAt = one.place(this, outAt); + + if (placedAt < outAt) { + throw new RuntimeException("bogus place() result for " + + one); + } + + outAt = placedAt + one.writeSize(); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, + "...while placing " + one); + } + } + + writeSize = outAt; + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(AnnotatedOutput out) { + boolean annotates = out.annotates(); + boolean first = true; + DexFile file = getFile(); + int at = 0; + + for (OffsettedItem one : items) { + if (annotates) { + if (first) { + first = false; + } else { + out.annotate(0, "\n"); + } + } + + int alignMask = one.getAlignment() - 1; + int writeAt = (at + alignMask) & ~alignMask; + + if (at != writeAt) { + out.writeZeroes(writeAt - at); + at = writeAt; + } + + one.writeTo(file, out); + at += one.writeSize(); + } + + if (at != writeSize) { + throw new RuntimeException("output size mismatch"); + } + } +} |