diff options
Diffstat (limited to 'dexgen/src/com/android/dexgen/dex/file/Section.java')
-rw-r--r-- | dexgen/src/com/android/dexgen/dex/file/Section.java | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/dexgen/src/com/android/dexgen/dex/file/Section.java b/dexgen/src/com/android/dexgen/dex/file/Section.java new file mode 100644 index 0000000..7efaf6b --- /dev/null +++ b/dexgen/src/com/android/dexgen/dex/file/Section.java @@ -0,0 +1,287 @@ +/* + * 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 java.util.Collection; + +/** + * A section of a {@code .dex} file. Each section consists of a list + * of items of some sort or other. + */ +public abstract class Section { + /** {@code null-ok;} name of this part, for annotation purposes */ + private final String name; + + /** {@code non-null;} file that this instance is part of */ + private final DexFile file; + + /** {@code > 0;} alignment requirement for the final output; + * must be a power of 2 */ + private final int alignment; + + /** {@code >= -1;} offset from the start of the file to this part, or + * {@code -1} if not yet known */ + private int fileOffset; + + /** whether {@link #prepare} has been called successfully on this + * instance */ + private boolean prepared; + + /** + * Validates an alignment. + * + * @param alignment the alignment + * @throws IllegalArgumentException thrown if {@code alignment} + * isn't a positive power of 2 + */ + public static void validateAlignment(int alignment) { + if ((alignment <= 0) || + (alignment & (alignment - 1)) != 0) { + throw new IllegalArgumentException("invalid alignment"); + } + } + + /** + * 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 + */ + public Section(String name, DexFile file, int alignment) { + if (file == null) { + throw new NullPointerException("file == null"); + } + + validateAlignment(alignment); + + this.name = name; + this.file = file; + this.alignment = alignment; + this.fileOffset = -1; + this.prepared = false; + } + + /** + * Gets the file that this instance is part of. + * + * @return {@code non-null;} the file + */ + public final DexFile getFile() { + return file; + } + + /** + * Gets the alignment for this instance's final output. + * + * @return {@code > 0;} the alignment + */ + public final int getAlignment() { + return alignment; + } + + /** + * Gets the offset from the start of the file to this part. This + * throws an exception if the offset has not yet been set. + * + * @return {@code >= 0;} the file offset + */ + public final int getFileOffset() { + if (fileOffset < 0) { + throw new RuntimeException("fileOffset not set"); + } + + return fileOffset; + } + + /** + * Sets the file offset. It is only valid to call this method once + * once per instance. + * + * @param fileOffset {@code >= 0;} the desired offset from the start of the + * file where this for this instance + * @return {@code >= 0;} the offset that this instance should be placed at + * in order to meet its alignment constraint + */ + public final int setFileOffset(int fileOffset) { + if (fileOffset < 0) { + throw new IllegalArgumentException("fileOffset < 0"); + } + + if (this.fileOffset >= 0) { + throw new RuntimeException("fileOffset already set"); + } + + int mask = alignment - 1; + fileOffset = (fileOffset + mask) & ~mask; + + this.fileOffset = fileOffset; + + return fileOffset; + } + + /** + * Writes this instance to the given raw data object. + * + * @param out {@code non-null;} where to write to + */ + public final void writeTo(AnnotatedOutput out) { + throwIfNotPrepared(); + align(out); + + int cursor = out.getCursor(); + + if (fileOffset < 0) { + fileOffset = cursor; + } else if (fileOffset != cursor) { + throw new RuntimeException("alignment mismatch: for " + this + + ", at " + cursor + + ", but expected " + fileOffset); + } + + if (out.annotates()) { + if (name != null) { + out.annotate(0, "\n" + name + ":"); + } else if (cursor != 0) { + out.annotate(0, "\n"); + } + } + + writeTo0(out); + } + + /** + * Returns the absolute file offset, given an offset from the + * start of this instance's output. This is only valid to call + * once this instance has been assigned a file offset (via {@link + * #setFileOffset}). + * + * @param relative {@code >= 0;} the relative offset + * @return {@code >= 0;} the corresponding absolute file offset + */ + public final int getAbsoluteOffset(int relative) { + if (relative < 0) { + throw new IllegalArgumentException("relative < 0"); + } + + if (fileOffset < 0) { + throw new RuntimeException("fileOffset not yet set"); + } + + return fileOffset + relative; + } + + /** + * Returns the absolute file offset of the given item which must + * be contained in this section. This is only valid to call + * once this instance has been assigned a file offset (via {@link + * #setFileOffset}). + * + * <p><b>Note:</b> Subclasses must implement this as appropriate for + * their contents.</p> + * + * @param item {@code non-null;} the item in question + * @return {@code >= 0;} the item's absolute file offset + */ + public abstract int getAbsoluteItemOffset(Item item); + + /** + * Prepares this instance for writing. This performs any necessary + * prerequisites, including particularly adding stuff to other + * sections. This method may only be called once per instance; + * subsequent calls will throw an exception. + */ + public final void prepare() { + throwIfPrepared(); + prepare0(); + prepared = true; + } + + /** + * Gets the collection of all the items in this section. + * It is not valid to attempt to change the returned list. + * + * @return {@code non-null;} the items + */ + public abstract Collection<? extends Item> items(); + + /** + * Does the main work of {@link #prepare}. + */ + protected abstract void prepare0(); + + /** + * Gets the size of this instance when output, in bytes. + * + * @return {@code >= 0;} the size of this instance, in bytes + */ + public abstract int writeSize(); + + /** + * Throws an exception if {@link #prepare} has not been + * called on this instance. + */ + protected final void throwIfNotPrepared() { + if (!prepared) { + throw new RuntimeException("not prepared"); + } + } + + /** + * Throws an exception if {@link #prepare} has already been called + * on this instance. + */ + protected final void throwIfPrepared() { + if (prepared) { + throw new RuntimeException("already prepared"); + } + } + + /** + * Aligns the output of the given data to the alignment of this instance. + * + * @param out {@code non-null;} the output to align + */ + protected final void align(AnnotatedOutput out) { + out.alignTo(alignment); + } + + /** + * Writes this instance to the given raw data object. This gets + * called by {@link #writeTo} after aligning the cursor of + * {@code out} and verifying that either the assigned file + * offset matches the actual cursor {@code out} or that the + * file offset was not previously assigned, in which case it gets + * assigned to {@code out}'s cursor. + * + * @param out {@code non-null;} where to write to + */ + protected abstract void writeTo0(AnnotatedOutput out); + + /** + * Returns the name of this section, for annotation purposes. + * + * @return {@code null-ok;} name of this part, for annotation purposes + */ + protected final String getName() { + return name; + } +} |