blob: 7538027fc44ec456a497ee503c68fe5e3385dd18 [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.jack.dx.rop.code;
import com.android.jack.dx.util.MutabilityControl;
/**
* Set of {@link RegisterSpec} instances, where a given register number
* may appear only once in the set.
*/
public final class RegisterSpecSet extends MutabilityControl {
/** {@code non-null;} no-element instance */
public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0);
/**
* {@code non-null;} array of register specs, where each element is
* {@code null} or is an instance whose {@code reg}
* matches the array index
*/
private final RegisterSpec[] specs;
/** {@code >= -1;} size of the set or {@code -1} if not yet calculated */
private int size;
/**
* Constructs an instance. The instance is initially empty.
*
* @param maxSize {@code >= 0;} the maximum register number (exclusive) that
* may be represented in this instance
*/
public RegisterSpecSet(int maxSize) {
super(maxSize != 0);
this.specs = new RegisterSpec[maxSize];
this.size = 0;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (!(other instanceof RegisterSpecSet)) {
return false;
}
RegisterSpecSet otherSet = (RegisterSpecSet) other;
RegisterSpec[] otherSpecs = otherSet.specs;
int len = specs.length;
if ((len != otherSpecs.length) || (size() != otherSet.size())) {
return false;
}
for (int i = 0; i < len; i++) {
RegisterSpec s1 = specs[i];
RegisterSpec s2 = otherSpecs[i];
if (s1 == s2) {
continue;
}
if ((s1 == null) || !s1.equals(s2)) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int len = specs.length;
int hash = 0;
for (int i = 0; i < len; i++) {
RegisterSpec spec = specs[i];
int oneHash = (spec == null) ? 0 : spec.hashCode();
hash = (hash * 31) + oneHash;
}
return hash;
}
/** {@inheritDoc} */
@Override
public String toString() {
int len = specs.length;
StringBuffer sb = new StringBuffer(len * 25);
sb.append('{');
boolean any = false;
for (int i = 0; i < len; i++) {
RegisterSpec spec = specs[i];
if (spec != null) {
if (any) {
sb.append(", ");
} else {
any = true;
}
sb.append(spec);
}
}
sb.append('}');
return sb.toString();
}
/**
* Gets the maximum number of registers that may be in this instance, which
* is also the maximum-plus-one of register numbers that may be
* represented.
*
* @return {@code >= 0;} the maximum size
*/
public int getMaxSize() {
return specs.length;
}
/**
* Gets the current size of this instance.
*
* @return {@code >= 0;} the size
*/
public int size() {
int result = size;
if (result < 0) {
int len = specs.length;
result = 0;
for (int i = 0; i < len; i++) {
if (specs[i] != null) {
result++;
}
}
size = result;
}
return result;
}
/**
* Gets the element with the given register number, if any.
*
* @param reg {@code >= 0;} the desired register number
* @return {@code null-ok;} the element with the given register number or
* {@code null} if there is none
*/
public RegisterSpec get(int reg) {
try {
return specs[reg];
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus reg");
}
}
/**
* Gets the element with the same register number as the given
* spec, if any. This is just a convenient shorthand for
* {@code get(spec.getReg())}.
*
* @param spec {@code non-null;} spec with the desired register number
* @return {@code null-ok;} the element with the matching register number or
* {@code null} if there is none
*/
public RegisterSpec get(RegisterSpec spec) {
return get(spec.getReg());
}
/**
* Returns the spec in this set that's currently associated with a
* given local (type, name, and signature), or {@code null} if there is
* none. This ignores the register number of the given spec but
* matches on everything else.
*
* @param spec {@code non-null;} local to look for
* @return {@code null-ok;} first register found that matches, if any
*/
public RegisterSpec findMatchingLocal(RegisterSpec spec) {
int length = specs.length;
for (int reg = 0; reg < length; reg++) {
RegisterSpec s = specs[reg];
if (s == null) {
continue;
}
if (spec.matchesVariable(s)) {
return s;
}
}
return null;
}
/**
* Returns the spec in this set that's currently associated with a given
* local (name and signature), or {@code null} if there is none.
*
* @param local {@code non-null;} local item to search for
* @return {@code null-ok;} first register found with matching name and signature
*/
public RegisterSpec localItemToSpec(LocalItem local) {
int length = specs.length;
for (int reg = 0; reg < length; reg++) {
RegisterSpec spec = specs[reg];
if ((spec != null) && local.equals(spec.getLocalItem())) {
return spec;
}
}
return null;
}
/**
* Removes a spec from the set. Only the register number
* of the parameter is significant.
*
* @param toRemove {@code non-null;} register to remove.
*/
public void remove(RegisterSpec toRemove) {
try {
specs[toRemove.getReg()] = null;
size = -1;
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus reg");
}
}
/**
* Puts the given spec into the set. If there is already an element in
* the set with the same register number, it is replaced. Additionally,
* if the previous element is for a category-2 register, then that
* previous element is nullified. Finally, if the given spec is for
* a category-2 register, then the immediately subsequent element
* is nullified.
*
* @param spec {@code non-null;} the register spec to put in the instance
*/
public void put(RegisterSpec spec) {
throwIfImmutable();
if (spec == null) {
throw new NullPointerException("spec == null");
}
size = -1;
try {
int reg = spec.getReg();
specs[reg] = spec;
if (reg > 0) {
int prevReg = reg - 1;
RegisterSpec prevSpec = specs[prevReg];
if ((prevSpec != null) && (prevSpec.getCategory() == 2)) {
specs[prevReg] = null;
}
}
if (spec.getCategory() == 2) {
specs[reg + 1] = null;
}
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("spec.getReg() out of range");
}
}
/**
* Put the entire contents of the given set into this one.
*
* @param set {@code non-null;} the set to put into this instance
*/
public void putAll(RegisterSpecSet set) {
int max = set.getMaxSize();
for (int i = 0; i < max; i++) {
RegisterSpec spec = set.get(i);
if (spec != null) {
put(spec);
}
}
}
/**
* Intersects this instance with the given one, modifying this
* instance. The intersection consists of the pairwise
* {@link RegisterSpec#intersect} of corresponding elements from
* this instance and the given one where both are non-null.
*
* @param other {@code non-null;} set to intersect with
* @param localPrimary whether local variables are primary to
* the intersection; if {@code true}, then the only non-null
* result elements occur when registers being intersected have
* equal names (or both have {@code null} names)
*/
public void intersect(RegisterSpecSet other, boolean localPrimary) {
throwIfImmutable();
RegisterSpec[] otherSpecs = other.specs;
int thisLen = specs.length;
int len = Math.min(thisLen, otherSpecs.length);
size = -1;
for (int i = 0; i < len; i++) {
RegisterSpec spec = specs[i];
if (spec == null) {
continue;
}
RegisterSpec intersection = spec.intersect(otherSpecs[i], localPrimary);
if (intersection != spec) {
specs[i] = intersection;
}
}
for (int i = len; i < thisLen; i++) {
specs[i] = null;
}
}
/**
* Returns an instance that is identical to this one, except that
* all register numbers are offset by the given amount. Mutability
* of the result is inherited from the original.
*
* @param delta the amount to offset the register numbers by
* @return {@code non-null;} an appropriately-constructed instance
*/
public RegisterSpecSet withOffset(int delta) {
int len = specs.length;
RegisterSpecSet result = new RegisterSpecSet(len + delta);
for (int i = 0; i < len; i++) {
RegisterSpec spec = specs[i];
if (spec != null) {
result.put(spec.withOffset(delta));
}
}
result.size = size;
if (isImmutable()) {
result.setImmutable();
}
return result;
}
/**
* Makes and return a mutable copy of this instance.
*
* @return {@code non-null;} the mutable copy
*/
public RegisterSpecSet mutableCopy() {
int len = specs.length;
RegisterSpecSet copy = new RegisterSpecSet(len);
for (int i = 0; i < len; i++) {
RegisterSpec spec = specs[i];
if (spec != null) {
copy.put(spec);
}
}
copy.size = size;
return copy;
}
}