/dalvik/dexgen/src/com/android/dexgen/dex/file/MixedItemSection.java
Java | 362 lines | 202 code | 57 blank | 103 comment | 31 complexity | 9413f53420270371f95161547e7ec6a5 MD5 | raw file
- /*
- * 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.dexgen.dex.file;
- import com.android.dexgen.util.AnnotatedOutput;
- import com.android.dexgen.util.ExceptionWithContext;
- import com.android.dexgen.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");
- }
- }
- }