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.cf.code; 18 19 import com.android.dex.util.ExceptionWithContext; 20 import com.android.dx.rop.code.RegisterSpec; 21 import com.android.dx.rop.type.Type; 22 import com.android.dx.rop.type.TypeBearer; 23 import com.android.dx.util.Hex; 24 import java.util.ArrayList; 25 26 /** 27 * Representation of a set of local variable arrays, with Java semantics. 28 * This peculiar case is to support in-method subroutines, which can 29 * have different locals sets for each caller. 30 * 31 * <p><b>Note:</b> For the most part, the documentation for this class 32 * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link 33 * com.android.dx.rop.type.TypeBearer}.</p> 34 */ 35 public class LocalsArraySet extends LocalsArray { 36 37 /** 38 * The primary LocalsArray represents the locals as seen from 39 * the subroutine itself, which is the merged representation of all the 40 * individual locals states. 41 */ 42 private final OneLocalsArray primary; 43 44 /** 45 * Indexed by label of caller block: the locals specific to each caller's 46 * invocation of the subroutine. 47 */ 48 private final ArrayList<LocalsArray> secondaries; 49 50 /** 51 * Constructs an instance. The locals array initially consists of 52 * all-uninitialized values (represented as {@code null}s). 53 * 54 * @param maxLocals {@code >= 0;} the maximum number of locals this instance 55 * can refer to 56 */ LocalsArraySet(int maxLocals)57 public LocalsArraySet(int maxLocals) { 58 super(maxLocals != 0); 59 primary = new OneLocalsArray(maxLocals); 60 secondaries = new ArrayList(); 61 } 62 63 /** 64 * Constructs an instance with the specified primary and secondaries set. 65 * 66 * @param primary {@code non-null;} primary locals to use 67 * @param secondaries {@code non-null;} secondaries set, indexed by subroutine 68 * caller label. 69 */ LocalsArraySet(OneLocalsArray primary, ArrayList<LocalsArray> secondaries)70 public LocalsArraySet(OneLocalsArray primary, 71 ArrayList<LocalsArray> secondaries) { 72 super(primary.getMaxLocals() > 0); 73 74 this.primary = primary; 75 this.secondaries = secondaries; 76 } 77 78 /** 79 * Constructs an instance which is a copy of another. 80 * 81 * @param toCopy {@code non-null;} instance to copy. 82 */ LocalsArraySet(LocalsArraySet toCopy)83 private LocalsArraySet(LocalsArraySet toCopy) { 84 super(toCopy.getMaxLocals() > 0); 85 86 primary = toCopy.primary.copy(); 87 secondaries = new ArrayList(toCopy.secondaries.size()); 88 89 int sz = toCopy.secondaries.size(); 90 for (int i = 0; i < sz; i++) { 91 LocalsArray la = toCopy.secondaries.get(i); 92 93 if (la == null) { 94 secondaries.add(null); 95 } else { 96 secondaries.add(la.copy()); 97 } 98 } 99 } 100 101 102 /** @inheritDoc */ 103 @Override setImmutable()104 public void setImmutable() { 105 primary.setImmutable(); 106 107 for (LocalsArray la : secondaries) { 108 if (la != null) { 109 la.setImmutable(); 110 } 111 } 112 super.setImmutable(); 113 } 114 115 /** @inheritDoc */ 116 @Override copy()117 public LocalsArray copy() { 118 return new LocalsArraySet(this); 119 } 120 121 /** @inheritDoc */ 122 @Override annotate(ExceptionWithContext ex)123 public void annotate(ExceptionWithContext ex) { 124 ex.addContext("(locals array set; primary)"); 125 primary.annotate(ex); 126 127 int sz = secondaries.size(); 128 for (int label = 0; label < sz; label++) { 129 LocalsArray la = secondaries.get(label); 130 131 if (la != null) { 132 ex.addContext("(locals array set: primary for caller " 133 + Hex.u2(label) + ')'); 134 135 la.getPrimary().annotate(ex); 136 } 137 } 138 } 139 140 /** {@inheritDoc*/ toHuman()141 public String toHuman() { 142 StringBuilder sb = new StringBuilder(); 143 144 sb.append("(locals array set; primary)\n"); 145 146 sb.append(getPrimary().toHuman()); 147 sb.append('\n'); 148 149 int sz = secondaries.size(); 150 for (int label = 0; label < sz; label++) { 151 LocalsArray la = secondaries.get(label); 152 153 if (la != null) { 154 sb.append("(locals array set: primary for caller " 155 + Hex.u2(label) + ")\n"); 156 157 sb.append(la.getPrimary().toHuman()); 158 sb.append('\n'); 159 } 160 } 161 162 return sb.toString(); 163 } 164 165 /** @inheritDoc */ 166 @Override makeInitialized(Type type)167 public void makeInitialized(Type type) { 168 int len = primary.getMaxLocals(); 169 170 if (len == 0) { 171 // We have to check for this before checking for immutability. 172 return; 173 } 174 175 throwIfImmutable(); 176 177 primary.makeInitialized(type); 178 179 for (LocalsArray la : secondaries) { 180 if (la != null) { 181 la.makeInitialized(type); 182 } 183 } 184 } 185 186 /** @inheritDoc */ 187 @Override getMaxLocals()188 public int getMaxLocals() { 189 return primary.getMaxLocals(); 190 } 191 192 /** @inheritDoc */ 193 @Override set(int idx, TypeBearer type)194 public void set(int idx, TypeBearer type) { 195 throwIfImmutable(); 196 197 primary.set(idx, type); 198 199 for (LocalsArray la : secondaries) { 200 if (la != null) { 201 la.set(idx, type); 202 } 203 } 204 } 205 206 /** @inheritDoc */ 207 @Override set(RegisterSpec spec)208 public void set(RegisterSpec spec) { 209 set(spec.getReg(), spec); 210 } 211 212 /** @inheritDoc */ 213 @Override invalidate(int idx)214 public void invalidate(int idx) { 215 throwIfImmutable(); 216 217 primary.invalidate(idx); 218 219 for (LocalsArray la : secondaries) { 220 if (la != null) { 221 la.invalidate(idx); 222 } 223 } 224 } 225 226 /** @inheritDoc */ 227 @Override getOrNull(int idx)228 public TypeBearer getOrNull(int idx) { 229 return primary.getOrNull(idx); 230 } 231 232 /** @inheritDoc */ 233 @Override get(int idx)234 public TypeBearer get(int idx) { 235 return primary.get(idx); 236 } 237 238 /** @inheritDoc */ 239 @Override getCategory1(int idx)240 public TypeBearer getCategory1(int idx) { 241 return primary.getCategory1(idx); 242 } 243 244 /** @inheritDoc */ 245 @Override getCategory2(int idx)246 public TypeBearer getCategory2(int idx) { 247 return primary.getCategory2(idx); 248 } 249 250 /** 251 * Merges this set with another {@code LocalsArraySet} instance. 252 * 253 * @param other {@code non-null;} to merge 254 * @return {@code non-null;} this instance if merge was a no-op, or 255 * new merged instance. 256 */ mergeWithSet(LocalsArraySet other)257 private LocalsArraySet mergeWithSet(LocalsArraySet other) { 258 OneLocalsArray newPrimary; 259 ArrayList<LocalsArray> newSecondaries; 260 boolean secondariesChanged = false; 261 262 newPrimary = primary.merge(other.getPrimary()); 263 264 int sz1 = secondaries.size(); 265 int sz2 = other.secondaries.size(); 266 int sz = Math.max(sz1, sz2); 267 newSecondaries = new ArrayList(sz); 268 269 for (int i = 0; i < sz; i++) { 270 LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null); 271 LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null); 272 LocalsArray resultla = null; 273 274 if (la1 == la2) { 275 resultla = la1; 276 } else if (la1 == null) { 277 resultla = la2; 278 } else if (la2 == null) { 279 resultla = la1; 280 } else { 281 try { 282 resultla = la1.merge(la2); 283 } catch (SimException ex) { 284 ex.addContext( 285 "Merging locals set for caller block " + Hex.u2(i)); 286 } 287 } 288 289 secondariesChanged = secondariesChanged || (la1 != resultla); 290 291 newSecondaries.add(resultla); 292 } 293 294 if ((primary == newPrimary) && ! secondariesChanged ) { 295 return this; 296 } 297 298 return new LocalsArraySet(newPrimary, newSecondaries); 299 } 300 301 /** 302 * Merges this set with a {@code OneLocalsArray} instance. 303 * 304 * @param other {@code non-null;} to merge 305 * @return {@code non-null;} this instance if merge was a no-op, or 306 * new merged instance. 307 */ mergeWithOne(OneLocalsArray other)308 private LocalsArraySet mergeWithOne(OneLocalsArray other) { 309 OneLocalsArray newPrimary; 310 ArrayList<LocalsArray> newSecondaries; 311 boolean secondariesChanged = false; 312 313 newPrimary = primary.merge(other.getPrimary()); 314 newSecondaries = new ArrayList(secondaries.size()); 315 316 int sz = secondaries.size(); 317 for (int i = 0; i < sz; i++) { 318 LocalsArray la = secondaries.get(i); 319 LocalsArray resultla = null; 320 321 if (la != null) { 322 try { 323 resultla = la.merge(other); 324 } catch (SimException ex) { 325 ex.addContext("Merging one locals against caller block " 326 + Hex.u2(i)); 327 } 328 } 329 330 secondariesChanged = secondariesChanged || (la != resultla); 331 332 newSecondaries.add(resultla); 333 } 334 335 if ((primary == newPrimary) && ! secondariesChanged ) { 336 return this; 337 } 338 339 return new LocalsArraySet(newPrimary, newSecondaries); 340 } 341 342 /** @inheritDoc */ 343 @Override merge(LocalsArray other)344 public LocalsArraySet merge(LocalsArray other) { 345 LocalsArraySet result; 346 347 try { 348 if (other instanceof LocalsArraySet) { 349 result = mergeWithSet((LocalsArraySet) other); 350 } else { 351 result = mergeWithOne((OneLocalsArray) other); 352 } 353 } catch (SimException ex) { 354 ex.addContext("underlay locals:"); 355 annotate(ex); 356 ex.addContext("overlay locals:"); 357 other.annotate(ex); 358 throw ex; 359 } 360 361 result.setImmutable(); 362 return result; 363 } 364 365 /** 366 * Gets the {@code LocalsArray} instance for a specified subroutine 367 * caller label, or null if label has no locals associated with it. 368 * 369 * @param label {@code >= 0;} subroutine caller label 370 * @return {@code null-ok;} locals if available. 371 */ getSecondaryForLabel(int label)372 private LocalsArray getSecondaryForLabel(int label) { 373 if (label >= secondaries.size()) { 374 return null; 375 } 376 377 return secondaries.get(label); 378 } 379 380 /** {@inheritDoc} */ 381 @Override mergeWithSubroutineCaller(LocalsArray other, int predLabel)382 public LocalsArraySet mergeWithSubroutineCaller 383 (LocalsArray other, int predLabel) { 384 385 LocalsArray mine = getSecondaryForLabel(predLabel); 386 LocalsArray newSecondary; 387 OneLocalsArray newPrimary; 388 389 newPrimary = primary.merge(other.getPrimary()); 390 391 if (mine == other) { 392 newSecondary = mine; 393 } else if (mine == null) { 394 newSecondary = other; 395 } else { 396 newSecondary = mine.merge(other); 397 } 398 399 if ((newSecondary == mine) && (newPrimary == primary)) { 400 return this; 401 } else { 402 /* 403 * We're going to re-build a primary as a merge of all the 404 * secondaries. 405 */ 406 newPrimary = null; 407 408 int szSecondaries = secondaries.size(); 409 int sz = Math.max(predLabel + 1, szSecondaries); 410 ArrayList<LocalsArray> newSecondaries = new ArrayList(sz); 411 for (int i = 0; i < sz; i++) { 412 LocalsArray la = null; 413 414 if (i == predLabel) { 415 /* 416 * This LocalsArray always replaces any existing one, 417 * since this is the result of a refined iteration. 418 */ 419 la = newSecondary; 420 } else if (i < szSecondaries) { 421 la = secondaries.get(i); 422 } 423 424 if (la != null) { 425 if (newPrimary == null) { 426 newPrimary = la.getPrimary(); 427 } else { 428 newPrimary = newPrimary.merge(la.getPrimary()); 429 } 430 } 431 432 newSecondaries.add(la); 433 } 434 435 LocalsArraySet result 436 = new LocalsArraySet(newPrimary, newSecondaries); 437 result.setImmutable(); 438 return result; 439 } 440 } 441 442 /** 443 * Returns a LocalsArray instance representing the locals state that should 444 * be used when returning to a subroutine caller. 445 * 446 * @param subLabel {@code >= 0;} A calling label of a subroutine 447 * @return {@code null-ok;} an instance for this subroutine, or null if subroutine 448 * is not in this set. 449 */ subArrayForLabel(int subLabel)450 public LocalsArray subArrayForLabel(int subLabel) { 451 LocalsArray result = getSecondaryForLabel(subLabel); 452 return result; 453 } 454 455 /**{@inheritDoc}*/ 456 @Override getPrimary()457 protected OneLocalsArray getPrimary() { 458 return primary; 459 } 460 } 461