RegisterSpecSet.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.rop.code; 18 19import com.android.dx.util.MutabilityControl; 20import com.android.dx.rop.cst.CstUtf8; 21 22/** 23 * Set of {@link RegisterSpec} instances, where a given register number 24 * may appear only once in the set. 25 */ 26public final class RegisterSpecSet 27 extends MutabilityControl { 28 /** non-null; no-element instance */ 29 public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0); 30 31 /** 32 * non-null; array of register specs, where each element is 33 * <code>null</code> or is an instance whose <code>reg</code> 34 * matches the array index 35 */ 36 private final RegisterSpec[] specs; 37 38 /** >= -1; size of the set or <code>-1</code> if not yet calculated */ 39 private int size; 40 41 /** 42 * Constructs an instance. The instance is initially empty. 43 * 44 * @param maxSize >= 0; the maximum register number (exclusive) that 45 * may be represented in this instance 46 */ 47 public RegisterSpecSet(int maxSize) { 48 super(maxSize != 0); 49 50 this.specs = new RegisterSpec[maxSize]; 51 this.size = 0; 52 } 53 54 /** {@inheritDoc} */ 55 @Override 56 public boolean equals(Object other) { 57 if (!(other instanceof RegisterSpecSet)) { 58 return false; 59 } 60 61 RegisterSpecSet otherSet = (RegisterSpecSet) other; 62 RegisterSpec[] otherSpecs = otherSet.specs; 63 int len = specs.length; 64 65 if ((len != otherSpecs.length) || (size() != otherSet.size())) { 66 return false; 67 } 68 69 for (int i = 0; i < len; i++) { 70 RegisterSpec s1 = specs[i]; 71 RegisterSpec s2 = otherSpecs[i]; 72 73 if (s1 == s2) { 74 continue; 75 } 76 77 if ((s1 == null) || !s1.equals(s2)) { 78 return false; 79 } 80 } 81 82 return true; 83 } 84 85 /** {@inheritDoc} */ 86 @Override 87 public int hashCode() { 88 int len = specs.length; 89 int hash = 0; 90 91 for (int i = 0; i < len; i++) { 92 RegisterSpec spec = specs[i]; 93 int oneHash = (spec == null) ? 0 : spec.hashCode(); 94 hash = (hash * 31) + oneHash; 95 } 96 97 return hash; 98 } 99 100 /** {@inheritDoc} */ 101 @Override 102 public String toString() { 103 int len = specs.length; 104 StringBuffer sb = new StringBuffer(len * 25); 105 106 sb.append('{'); 107 108 boolean any = false; 109 for (int i = 0; i < len; i++) { 110 RegisterSpec spec = specs[i]; 111 if (spec != null) { 112 if (any) { 113 sb.append(", "); 114 } else { 115 any = true; 116 } 117 sb.append(spec); 118 } 119 } 120 121 sb.append('}'); 122 return sb.toString(); 123 } 124 125 /** 126 * Gets the maximum number of registers that may be in this instance, which 127 * is also the maximum-plus-one of register numbers that may be 128 * represented. 129 * 130 * @return >= 0; the maximum size 131 */ 132 public int getMaxSize() { 133 return specs.length; 134 } 135 136 /** 137 * Gets the current size of this instance. 138 * 139 * @return >= 0; the size 140 */ 141 public int size() { 142 int result = size; 143 144 if (result < 0) { 145 int len = specs.length; 146 147 result = 0; 148 for (int i = 0; i < len; i++) { 149 if (specs[i] != null) { 150 result++; 151 } 152 } 153 154 size = result; 155 } 156 157 return result; 158 } 159 160 /** 161 * Gets the element with the given register number, if any. 162 * 163 * @param reg >= 0; the desired register number 164 * @return null-ok; the element with the given register number or 165 * <code>null</code> if there is none 166 */ 167 public RegisterSpec get(int reg) { 168 try { 169 return specs[reg]; 170 } catch (ArrayIndexOutOfBoundsException ex) { 171 // Translate the exception. 172 throw new IllegalArgumentException("bogus reg"); 173 } 174 } 175 176 /** 177 * Gets the element with the same register number as the given 178 * spec, if any. This is just a convenient shorthand for 179 * <code>get(spec.getReg())</code>. 180 * 181 * @param spec non-null; spec with the desired register number 182 * @return null-ok; the element with the matching register number or 183 * <code>null</code> if there is none 184 */ 185 public RegisterSpec get(RegisterSpec spec) { 186 return get(spec.getReg()); 187 } 188 189 /** 190 * Returns the spec in this set that's currently associated with a given 191 * name, or null if there is none. 192 * 193 * @param local non-null; local item to search for 194 * @return null-ok; first register found with name. 195 */ 196 public RegisterSpec localItemToSpec(LocalItem local) { 197 for (int reg = 0; reg < specs.length; reg++) { 198 if (specs[reg] != null && local.equals(specs[reg].getLocalItem())) { 199 return specs[reg]; 200 } 201 } 202 203 return null; 204 } 205 206 /** 207 * Removes a spec from the set. Only the register number 208 * of the parameter is significant. 209 * 210 * @param toRemove non-null; register to remove. 211 */ 212 public void remove(RegisterSpec toRemove) { 213 try { 214 specs[toRemove.getReg()] = null; 215 size = -1; 216 } catch (ArrayIndexOutOfBoundsException ex) { 217 // Translate the exception. 218 throw new IllegalArgumentException("bogus reg"); 219 } 220 } 221 222 /** 223 * Puts the given spec into the set. If there is already an element in 224 * the set with the same register number, it is replaced. Additionally, 225 * if the previous element is for a category-2 register, then that 226 * previous element is nullified. Finally, if the given spec is for 227 * a category-2 register, then the immediately subsequent element 228 * is nullified. 229 * 230 * @param spec non-null; the register spec to put in the instance 231 */ 232 public void put(RegisterSpec spec) { 233 throwIfImmutable(); 234 235 if (spec == null) { 236 throw new NullPointerException("spec == null"); 237 } 238 239 size = -1; 240 241 try { 242 int reg = spec.getReg(); 243 specs[reg] = spec; 244 245 if (reg > 0) { 246 int prevReg = reg - 1; 247 RegisterSpec prevSpec = specs[prevReg]; 248 if ((prevSpec != null) && (prevSpec.getCategory() == 2)) { 249 specs[prevReg] = null; 250 } 251 } 252 253 if (spec.getCategory() == 2) { 254 specs[reg + 1] = null; 255 } 256 } catch (ArrayIndexOutOfBoundsException ex) { 257 // Translate the exception. 258 throw new IllegalArgumentException("spec.getReg() out of range"); 259 } 260 } 261 262 /** 263 * Intersects this instance with the given one, modifying this 264 * instance. The intersection consists of the pairwise 265 * {@link RegisterSpec#intersect} of corresponding elements from 266 * this instance and the given one where both are non-null. 267 * 268 * @param other non-null; set to intersect with 269 * @param localPrimary whether local variables are primary to 270 * the intersection; if <code>true</code>, then the only non-null 271 * result elements occur when registers being intersected have 272 * equal names (or both have <code>null</code> names) 273 */ 274 public void intersect(RegisterSpecSet other, boolean localPrimary) { 275 throwIfImmutable(); 276 277 RegisterSpec[] otherSpecs = other.specs; 278 int thisLen = specs.length; 279 int len = Math.min(thisLen, otherSpecs.length); 280 281 size = -1; 282 283 for (int i = 0; i < len; i++) { 284 RegisterSpec spec = specs[i]; 285 286 if (spec == null) { 287 continue; 288 } 289 290 RegisterSpec intersection = 291 spec.intersect(otherSpecs[i], localPrimary); 292 if (intersection != spec) { 293 specs[i] = intersection; 294 } 295 } 296 297 for (int i = len; i < thisLen; i++) { 298 specs[i] = null; 299 } 300 } 301 302 /** 303 * Returns an instance that is identical to this one, except that 304 * all register numbers are offset by the given amount. Mutability 305 * of the result is inherited from the original. 306 * 307 * @param delta the amount to offset the register numbers by 308 * @return non-null; an appropriately-constructed instance 309 */ 310 public RegisterSpecSet withOffset(int delta) { 311 int len = specs.length; 312 RegisterSpecSet result = new RegisterSpecSet(len + delta); 313 314 for (int i = 0; i < len; i++) { 315 RegisterSpec spec = specs[i]; 316 if (spec != null) { 317 result.put(spec.withOffset(delta)); 318 } 319 } 320 321 result.size = size; 322 323 if (isImmutable()) { 324 result.setImmutable(); 325 } 326 327 return result; 328 } 329 330 /** 331 * Makes and return a mutable copy of this instance. 332 * 333 * @return non-null; the mutable copy 334 */ 335 public RegisterSpecSet mutableCopy() { 336 int len = specs.length; 337 RegisterSpecSet copy = new RegisterSpecSet(len); 338 339 for (int i = 0; i < len; i++) { 340 RegisterSpec spec = specs[i]; 341 if (spec != null) { 342 copy.put(spec); 343 } 344 } 345 346 copy.size = size; 347 348 return copy; 349 } 350} 351