blob: 4a0bae10ca996b24cd24f12a9cd4a9be5c83ce64 [file] [log] [blame]
/*
* 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.
*
* <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, 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. <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 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.
*
* <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, 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);
}
}
}