| /* |
| * 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.cf.code; |
| |
| import com.android.dx.rop.cst.CstUtf8; |
| import com.android.dx.rop.type.Type; |
| import com.android.dx.rop.code.LocalItem; |
| import com.android.dx.util.FixedSizeList; |
| |
| /** |
| * List of "local variable" entries, which are the contents of |
| * {@code LocalVariableTable} and {@code LocalVariableTypeTable} |
| * attributes, as well as combinations of the two. |
| */ |
| public final class LocalVariableList extends FixedSizeList { |
| /** {@code non-null;} zero-size instance */ |
| public static final LocalVariableList EMPTY = new LocalVariableList(0); |
| |
| /** |
| * Returns an instance which is the concatenation of the two given |
| * instances. The result is immutable. |
| * |
| * @param list1 {@code non-null;} first instance |
| * @param list2 {@code non-null;} second instance |
| * @return {@code non-null;} combined instance |
| */ |
| public static LocalVariableList concat(LocalVariableList list1, |
| LocalVariableList list2) { |
| if (list1 == EMPTY) { |
| // easy case |
| return list2; |
| } |
| |
| int sz1 = list1.size(); |
| int sz2 = list2.size(); |
| LocalVariableList result = new LocalVariableList(sz1 + sz2); |
| |
| for (int i = 0; i < sz1; i++) { |
| result.set(i, list1.get(i)); |
| } |
| |
| for (int i = 0; i < sz2; i++) { |
| result.set(sz1 + i, list2.get(i)); |
| } |
| |
| result.setImmutable(); |
| return result; |
| } |
| |
| /** |
| * Returns an instance which is the result of merging the two |
| * given instances, where one instance should have only type |
| * descriptors and the other only type signatures. The merged |
| * result is identical to the one with descriptors, except that |
| * any element whose {name, index, start, length} matches an |
| * element in the signature list gets augmented with the |
| * corresponding signature. The result is immutable. |
| * |
| * @param descriptorList {@code non-null;} list with descriptors |
| * @param signatureList {@code non-null;} list with signatures |
| * @return {@code non-null;} the merged result |
| */ |
| public static LocalVariableList mergeDescriptorsAndSignatures( |
| LocalVariableList descriptorList, |
| LocalVariableList signatureList) { |
| int descriptorSize = descriptorList.size(); |
| LocalVariableList result = new LocalVariableList(descriptorSize); |
| |
| for (int i = 0; i < descriptorSize; i++) { |
| Item item = descriptorList.get(i); |
| Item signatureItem = signatureList.itemToLocal(item); |
| if (signatureItem != null) { |
| CstUtf8 signature = signatureItem.getSignature(); |
| item = item.withSignature(signature); |
| } |
| result.set(i, item); |
| } |
| |
| result.setImmutable(); |
| return result; |
| } |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param count the number of elements to be in the list |
| */ |
| public LocalVariableList(int count) { |
| super(count); |
| } |
| |
| /** |
| * Gets the indicated item. |
| * |
| * @param n {@code >= 0;} which item |
| * @return {@code null-ok;} the indicated item |
| */ |
| public Item get(int n) { |
| return (Item) get0(n); |
| } |
| |
| /** |
| * Sets the item at the given index. |
| * |
| * @param n {@code >= 0, < size();} which element |
| * @param item {@code non-null;} the item |
| */ |
| public void set(int n, Item item) { |
| if (item == null) { |
| throw new NullPointerException("item == null"); |
| } |
| |
| set0(n, item); |
| } |
| |
| /** |
| * Sets the item at the given index. |
| * |
| * <p><b>Note:</b> At least one of {@code descriptor} or |
| * {@code signature} must be passed as non-null.</p> |
| * |
| * @param n {@code >= 0, < size();} which element |
| * @param startPc {@code >= 0;} the start pc of this variable's scope |
| * @param length {@code >= 0;} the length (in bytecodes) of this variable's |
| * scope |
| * @param name {@code non-null;} the variable's name |
| * @param descriptor {@code null-ok;} the variable's type descriptor |
| * @param signature {@code null-ok;} the variable's type signature |
| * @param index {@code >= 0;} the variable's local index |
| */ |
| public void set(int n, int startPc, int length, CstUtf8 name, |
| CstUtf8 descriptor, CstUtf8 signature, int index) { |
| set0(n, new Item(startPc, length, name, descriptor, signature, index)); |
| } |
| |
| /** |
| * Gets the local variable information in this instance which matches |
| * the given {@link com.android.dx.cf.code.LocalVariableList.Item} |
| * in all respects but the type descriptor and signature, if any. |
| * |
| * @param item {@code non-null;} local variable information to match |
| * @return {@code null-ok;} the corresponding local variable information stored |
| * in this instance, or {@code null} if there is no matching |
| * information |
| */ |
| public Item itemToLocal(Item item) { |
| int sz = size(); |
| |
| for (int i = 0; i < sz; i++) { |
| Item one = (Item) get0(i); |
| |
| if ((one != null) && one.matchesAllButType(item)) { |
| return one; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Gets the local variable information associated with a given address |
| * and local index, if any. <b>Note:</b> In standard classfiles, a |
| * variable's start point is listed as the address of the instruction |
| * <i>just past</i> the one that sets the variable. |
| * |
| * @param pc {@code >= 0;} the address to look up |
| * @param index {@code >= 0;} the local variable index |
| * @return {@code null-ok;} the associated local variable information, or |
| * {@code null} if none is known |
| */ |
| public Item pcAndIndexToLocal(int pc, int index) { |
| int sz = size(); |
| |
| for (int i = 0; i < sz; i++) { |
| Item one = (Item) get0(i); |
| |
| if ((one != null) && one.matchesPcAndIndex(pc, index)) { |
| return one; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Item in a local variable table. |
| */ |
| public static class Item { |
| /** {@code >= 0;} the start pc of this variable's scope */ |
| private final int startPc; |
| |
| /** {@code >= 0;} the length (in bytecodes) of this variable's scope */ |
| private final int length; |
| |
| /** {@code non-null;} the variable's name */ |
| private final CstUtf8 name; |
| |
| /** {@code null-ok;} the variable's type descriptor */ |
| private final CstUtf8 descriptor; |
| |
| /** {@code null-ok;} the variable's type signature */ |
| private final CstUtf8 signature; |
| |
| /** {@code >= 0;} the variable's local index */ |
| private final int index; |
| |
| /** |
| * Constructs an instance. |
| * |
| * <p><b>Note:</b> At least one of {@code descriptor} or |
| * {@code signature} must be passed as non-null.</p> |
| * |
| * @param startPc {@code >= 0;} the start pc of this variable's scope |
| * @param length {@code >= 0;} the length (in bytecodes) of this variable's |
| * scope |
| * @param name {@code non-null;} the variable's name |
| * @param descriptor {@code null-ok;} the variable's type descriptor |
| * @param signature {@code null-ok;} the variable's type signature |
| * @param index {@code >= 0;} the variable's local index |
| */ |
| public Item(int startPc, int length, CstUtf8 name, |
| CstUtf8 descriptor, CstUtf8 signature, int index) { |
| if (startPc < 0) { |
| throw new IllegalArgumentException("startPc < 0"); |
| } |
| |
| if (length < 0) { |
| throw new IllegalArgumentException("length < 0"); |
| } |
| |
| if (name == null) { |
| throw new NullPointerException("name == null"); |
| } |
| |
| if ((descriptor == null) && (signature == null)) { |
| throw new NullPointerException( |
| "(descriptor == null) && (signature == null)"); |
| } |
| |
| if (index < 0) { |
| throw new IllegalArgumentException("index < 0"); |
| } |
| |
| this.startPc = startPc; |
| this.length = length; |
| this.name = name; |
| this.descriptor = descriptor; |
| this.signature = signature; |
| this.index = index; |
| } |
| |
| /** |
| * Gets the start pc of this variable's scope. |
| * |
| * @return {@code >= 0;} the start pc of this variable's scope |
| */ |
| public int getStartPc() { |
| return startPc; |
| } |
| |
| /** |
| * Gets the length (in bytecodes) of this variable's scope. |
| * |
| * @return {@code >= 0;} the length (in bytecodes) of this variable's scope |
| */ |
| public int getLength() { |
| return length; |
| } |
| |
| /** |
| * Gets the variable's type descriptor. |
| * |
| * @return {@code null-ok;} the variable's type descriptor |
| */ |
| public CstUtf8 getDescriptor() { |
| return descriptor; |
| } |
| |
| /** |
| * Gets the variable's LocalItem, a (name, signature) tuple |
| * |
| * @return {@code null-ok;} the variable's type descriptor |
| */ |
| public LocalItem getLocalItem() { |
| return LocalItem.make(name, signature); |
| } |
| |
| /** |
| * Gets the variable's type signature. Private because if you need this, |
| * you want getLocalItem() instead. |
| * |
| * @return {@code null-ok;} the variable's type signature |
| */ |
| private CstUtf8 getSignature() { |
| return signature; |
| } |
| |
| /** |
| * Gets the variable's local index. |
| * |
| * @return {@code >= 0;} the variable's local index |
| */ |
| public int getIndex() { |
| return index; |
| } |
| |
| /** |
| * Gets the variable's type descriptor. This is a convenient shorthand |
| * for {@code Type.intern(getDescriptor().getString())}. |
| * |
| * @return {@code non-null;} the variable's type |
| */ |
| public Type getType() { |
| return Type.intern(descriptor.getString()); |
| } |
| |
| /** |
| * Constructs and returns an instance which is identical to this |
| * one, except that the signature is changed to the given value. |
| * |
| * @param newSignature {@code non-null;} the new signature |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public Item withSignature(CstUtf8 newSignature) { |
| return new Item(startPc, length, name, descriptor, newSignature, |
| index); |
| } |
| |
| /** |
| * Gets whether this instance matches (describes) the given |
| * address and index. |
| * |
| * @param pc {@code >= 0;} the address in question |
| * @param index {@code >= 0;} the local variable index in question |
| * @return {@code true} iff this instance matches {@code pc} |
| * and {@code index} |
| */ |
| public boolean matchesPcAndIndex(int pc, int index) { |
| return (index == this.index) && |
| (pc >= startPc) && |
| (pc < (startPc + length)); |
| } |
| |
| /** |
| * Gets whether this instance matches (describes) the given |
| * other instance exactly in all fields except type descriptor and |
| * type signature. |
| * |
| * @param other {@code non-null;} the instance to compare to |
| * @return {@code true} iff this instance matches |
| */ |
| public boolean matchesAllButType(Item other) { |
| return (startPc == other.startPc) |
| && (length == other.length) |
| && (index == other.index) |
| && name.equals(other.name); |
| } |
| } |
| } |