summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/dex/file/MixedItemSection.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/dex/file/MixedItemSection.java')
-rw-r--r--dx/src/com/android/dx/dex/file/MixedItemSection.java362
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");
+ }
+ }
+}