/* * 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.rop.code; import com.android.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; } }