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