/* * 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.code.LocalItem; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.type.Type; 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) { CstString 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. * *

Note: At least one of {@code descriptor} or * {@code signature} must be passed as non-null.

* * @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, CstString name, CstString descriptor, CstString 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. Note: In standard classfiles, a * variable's start point is listed as the address of the instruction * just past 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 CstString name; /** {@code null-ok;} the variable's type descriptor */ private final CstString descriptor; /** {@code null-ok;} the variable's type signature */ private final CstString signature; /** {@code >= 0;} the variable's local index */ private final int index; /** * Constructs an instance. * *

Note: At least one of {@code descriptor} or * {@code signature} must be passed as non-null.

* * @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, CstString name, CstString descriptor, CstString 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 CstString 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 CstString 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(CstString 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); } } }