diff options
Diffstat (limited to 'dx/src/com/android/dx/dex/file/ClassDataItem.java')
-rw-r--r-- | dx/src/com/android/dx/dex/file/ClassDataItem.java | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/dex/file/ClassDataItem.java b/dx/src/com/android/dx/dex/file/ClassDataItem.java new file mode 100644 index 0000000..e9ae18b --- /dev/null +++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2008 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.rop.cst.Constant; +import com.android.dx.rop.cst.CstArray; +import com.android.dx.rop.cst.CstLiteralBits; +import com.android.dx.rop.cst.CstType; +import com.android.dx.rop.cst.Zeroes; +import com.android.dx.util.ByteArrayAnnotatedOutput; +import com.android.dx.util.AnnotatedOutput; +import com.android.dx.util.Writers; + +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * Representation of all the parts of a Dalvik class that are generally + * "inflated" into an in-memory representation at runtime. Instances of + * this class are represented in a compact streamable form in a + * {@code dex} file, as opposed to a random-access form. + */ +public final class ClassDataItem extends OffsettedItem { + /** {@code non-null;} what class this data is for, just for listing generation */ + private final CstType thisClass; + + /** {@code non-null;} list of static fields */ + private final ArrayList<EncodedField> staticFields; + + /** {@code non-null;} list of initial values for static fields */ + private final HashMap<EncodedField, Constant> staticValues; + + /** {@code non-null;} list of instance fields */ + private final ArrayList<EncodedField> instanceFields; + + /** {@code non-null;} list of direct methods */ + private final ArrayList<EncodedMethod> directMethods; + + /** {@code non-null;} list of virtual methods */ + private final ArrayList<EncodedMethod> virtualMethods; + + /** {@code null-ok;} static initializer list; set in {@link #addContents} */ + private CstArray staticValuesConstant; + + /** + * {@code null-ok;} encoded form, ready for writing to a file; set during + * {@link #place0} + */ + private byte[] encodedForm; + + /** + * Constructs an instance. Its sets of members are initially + * empty. + * + * @param thisClass {@code non-null;} what class this data is for, just + * for listing generation + */ + public ClassDataItem(CstType thisClass) { + super(1, -1); + + if (thisClass == null) { + throw new NullPointerException("thisClass == null"); + } + + this.thisClass = thisClass; + this.staticFields = new ArrayList<EncodedField>(20); + this.staticValues = new HashMap<EncodedField, Constant>(40); + this.instanceFields = new ArrayList<EncodedField>(20); + this.directMethods = new ArrayList<EncodedMethod>(20); + this.virtualMethods = new ArrayList<EncodedMethod>(20); + this.staticValuesConstant = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_CLASS_DATA_ITEM; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toString(); + } + + /** + * Returns whether this instance is empty. + * + * @return {@code true} if this instance is empty or + * {@code false} if at least one element has been added to it + */ + public boolean isEmpty() { + return staticFields.isEmpty() && instanceFields.isEmpty() + && directMethods.isEmpty() && virtualMethods.isEmpty(); + } + + /** + * Adds a static field. + * + * @param field {@code non-null;} the field to add + * @param value {@code null-ok;} initial value for the field, if any + */ + public void addStaticField(EncodedField field, Constant value) { + if (field == null) { + throw new NullPointerException("field == null"); + } + + if (staticValuesConstant != null) { + throw new UnsupportedOperationException( + "static fields already sorted"); + } + + staticFields.add(field); + staticValues.put(field, value); + } + + /** + * Adds an instance field. + * + * @param field {@code non-null;} the field to add + */ + public void addInstanceField(EncodedField field) { + if (field == null) { + throw new NullPointerException("field == null"); + } + + instanceFields.add(field); + } + + /** + * Adds a direct ({@code static} and/or {@code private}) method. + * + * @param method {@code non-null;} the method to add + */ + public void addDirectMethod(EncodedMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); + } + + directMethods.add(method); + } + + /** + * Adds a virtual method. + * + * @param method {@code non-null;} the method to add + */ + public void addVirtualMethod(EncodedMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); + } + + virtualMethods.add(method); + } + + /** + * Gets all the methods in this class. The returned list is not linked + * in any way to the underlying lists contained in this instance, but + * the objects contained in the list are shared. + * + * @return {@code non-null;} list of all methods + */ + public ArrayList<EncodedMethod> getMethods() { + int sz = directMethods.size() + virtualMethods.size(); + ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz); + + result.addAll(directMethods); + result.addAll(virtualMethods); + + return result; + } + + + /** + * Prints out the contents of this instance, in a debugging-friendly + * way. + * + * @param out {@code non-null;} where to output to + * @param verbose whether to be verbose with the output + */ + public void debugPrint(Writer out, boolean verbose) { + PrintWriter pw = Writers.printWriterFor(out); + + int sz = staticFields.size(); + for (int i = 0; i < sz; i++) { + pw.println(" sfields[" + i + "]: " + staticFields.get(i)); + } + + sz = instanceFields.size(); + for (int i = 0; i < sz; i++) { + pw.println(" ifields[" + i + "]: " + instanceFields.get(i)); + } + + sz = directMethods.size(); + for (int i = 0; i < sz; i++) { + pw.println(" dmeths[" + i + "]:"); + directMethods.get(i).debugPrint(pw, verbose); + } + + sz = virtualMethods.size(); + for (int i = 0; i < sz; i++) { + pw.println(" vmeths[" + i + "]:"); + virtualMethods.get(i).debugPrint(pw, verbose); + } + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + if (!staticFields.isEmpty()) { + getStaticValuesConstant(); // Force the fields to be sorted. + for (EncodedField field : staticFields) { + field.addContents(file); + } + } + + if (!instanceFields.isEmpty()) { + Collections.sort(instanceFields); + for (EncodedField field : instanceFields) { + field.addContents(file); + } + } + + if (!directMethods.isEmpty()) { + Collections.sort(directMethods); + for (EncodedMethod method : directMethods) { + method.addContents(file); + } + } + + if (!virtualMethods.isEmpty()) { + Collections.sort(virtualMethods); + for (EncodedMethod method : virtualMethods) { + method.addContents(file); + } + } + } + + /** + * Gets a {@link CstArray} corresponding to {@link #staticValues} if + * it contains any non-zero non-{@code null} values. + * + * @return {@code null-ok;} the corresponding constant or {@code null} if + * there are no values to encode + */ + public CstArray getStaticValuesConstant() { + if ((staticValuesConstant == null) && (staticFields.size() != 0)) { + staticValuesConstant = makeStaticValuesConstant(); + } + + return staticValuesConstant; + } + + /** + * Gets a {@link CstArray} corresponding to {@link #staticValues} if + * it contains any non-zero non-{@code null} values. + * + * @return {@code null-ok;} the corresponding constant or {@code null} if + * there are no values to encode + */ + private CstArray makeStaticValuesConstant() { + // First sort the statics into their final order. + Collections.sort(staticFields); + + /* + * Get the size of staticValues minus any trailing zeros/nulls (both + * nulls per se as well as instances of CstKnownNull). + */ + + int size = staticFields.size(); + while (size > 0) { + EncodedField field = staticFields.get(size - 1); + Constant cst = staticValues.get(field); + if (cst instanceof CstLiteralBits) { + // Note: CstKnownNull extends CstLiteralBits. + if (((CstLiteralBits) cst).getLongBits() != 0) { + break; + } + } else if (cst != null) { + break; + } + size--; + } + + if (size == 0) { + return null; + } + + // There is something worth encoding, so build up a result. + + CstArray.List list = new CstArray.List(size); + for (int i = 0; i < size; i++) { + EncodedField field = staticFields.get(i); + Constant cst = staticValues.get(field); + if (cst == null) { + cst = Zeroes.zeroFor(field.getRef().getType()); + } + list.set(i, cst); + } + list.setImmutable(); + + return new CstArray(list); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Encode the data and note the size. + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + + encodeOutput(addedTo.getFile(), out); + encodedForm = out.toByteArray(); + setWriteSize(encodedForm.length); + } + + /** + * Writes out the encoded form of this instance. + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + */ + private void encodeOutput(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + + if (annotates) { + out.annotate(0, offsetString() + " class data for " + + thisClass.toHuman()); + } + + encodeSize(file, out, "static_fields", staticFields.size()); + encodeSize(file, out, "instance_fields", instanceFields.size()); + encodeSize(file, out, "direct_methods", directMethods.size()); + encodeSize(file, out, "virtual_methods", virtualMethods.size()); + + encodeList(file, out, "static_fields", staticFields); + encodeList(file, out, "instance_fields", instanceFields); + encodeList(file, out, "direct_methods", directMethods); + encodeList(file, out, "virtual_methods", virtualMethods); + + if (annotates) { + out.endAnnotation(); + } + } + + /** + * Helper for {@link #encodeOutput}, which writes out the given + * size value, annotating it as well (if annotations are enabled). + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + * @param label {@code non-null;} the label for the purposes of annotation + * @param size {@code >= 0;} the size to write + */ + private static void encodeSize(DexFile file, AnnotatedOutput out, + String label, int size) { + if (out.annotates()) { + out.annotate(String.format(" %-21s %08x", label + "_size:", + size)); + } + + out.writeUleb128(size); + } + + /** + * Helper for {@link #encodeOutput}, which writes out the given + * list. It also annotates the items (if any and if annotations + * are enabled). + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + * @param label {@code non-null;} the label for the purposes of annotation + * @param list {@code non-null;} the list in question + */ + private static void encodeList(DexFile file, AnnotatedOutput out, + String label, ArrayList<? extends EncodedMember> list) { + int size = list.size(); + int lastIndex = 0; + + if (size == 0) { + return; + } + + if (out.annotates()) { + out.annotate(0, " " + label + ":"); + } + + for (int i = 0; i < size; i++) { + lastIndex = list.get(i).encode(file, out, lastIndex, i); + } + } + + /** {@inheritDoc} */ + @Override + public void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + + if (annotates) { + /* + * The output is to be annotated, so redo the work previously + * done by place0(), except this time annotations will actually + * get emitted. + */ + encodeOutput(file, out); + } else { + out.write(encodedForm); + } + } +} |