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.dx.rop.code.LocalItem; 20 import com.android.dx.rop.code.RegisterSpec; 21 import com.android.dx.rop.cst.Constant; 22 import com.android.dx.rop.type.Prototype; 23 import com.android.dx.rop.type.StdTypeList; 24 import com.android.dx.rop.type.Type; 25 import com.android.dx.rop.type.TypeBearer; 26 import java.util.ArrayList; 27 28 /** 29 * Base implementation of {@link Machine}. 30 * 31 * <p><b>Note:</b> For the most part, the documentation for this class 32 * ignores the distinction between {@link Type} and {@link 33 * TypeBearer}.</p> 34 */ 35 public abstract class BaseMachine implements Machine { 36 /* {@code non-null;} the prototype for the associated method */ 37 private final Prototype prototype; 38 39 /** {@code non-null;} primary arguments */ 40 private TypeBearer[] args; 41 42 /** {@code >= 0;} number of primary arguments */ 43 private int argCount; 44 45 /** {@code null-ok;} type of the operation, if salient */ 46 private Type auxType; 47 48 /** auxiliary {@code int} argument */ 49 private int auxInt; 50 51 /** {@code null-ok;} auxiliary constant argument */ 52 private Constant auxCst; 53 54 /** auxiliary branch target argument */ 55 private int auxTarget; 56 57 /** {@code null-ok;} auxiliary switch cases argument */ 58 private SwitchList auxCases; 59 60 /** {@code null-ok;} auxiliary initial value list for newarray */ 61 private ArrayList<Constant> auxInitValues; 62 63 /** {@code >= -1;} last local accessed */ 64 private int localIndex; 65 66 /** specifies if local has info in the local variable table */ 67 private boolean localInfo; 68 69 /** {@code null-ok;} local target spec, if salient and calculated */ 70 private RegisterSpec localTarget; 71 72 /** {@code non-null;} results */ 73 private TypeBearer[] results; 74 75 /** 76 * {@code >= -1;} count of the results, or {@code -1} if no results 77 * have been set 78 */ 79 private int resultCount; 80 81 /** 82 * Constructs an instance. 83 * 84 * @param prototype {@code non-null;} the prototype for the 85 * associated method 86 */ 87 public BaseMachine(Prototype prototype) { 88 if (prototype == null) { 89 throw new NullPointerException("prototype == null"); 90 } 91 92 this.prototype = prototype; 93 args = new TypeBearer[10]; 94 results = new TypeBearer[6]; 95 clearArgs(); 96 } 97 98 /** {@inheritDoc} */ 99 @Override 100 public Prototype getPrototype() { 101 return prototype; 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public final void clearArgs() { 107 argCount = 0; 108 auxType = null; 109 auxInt = 0; 110 auxCst = null; 111 auxTarget = 0; 112 auxCases = null; 113 auxInitValues = null; 114 localIndex = -1; 115 localInfo = false; 116 localTarget = null; 117 resultCount = -1; 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public final void popArgs(Frame frame, int count) { 123 ExecutionStack stack = frame.getStack(); 124 125 clearArgs(); 126 127 if (count > args.length) { 128 // Grow args, and add a little extra room to grow even more. 129 args = new TypeBearer[count + 10]; 130 } 131 132 for (int i = count - 1; i >= 0; i--) { 133 args[i] = stack.pop(); 134 } 135 136 argCount = count; 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public void popArgs(Frame frame, Prototype prototype) { 142 StdTypeList types = prototype.getParameterTypes(); 143 int size = types.size(); 144 145 // Use the above method to do the actual popping... 146 popArgs(frame, size); 147 148 // ...and then verify the popped types. 149 150 for (int i = 0; i < size; i++) { 151 if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) { 152 throw new SimException("at stack depth " + (size - 1 - i) + 153 ", expected type " + types.getType(i).toHuman() + 154 " but found " + args[i].getType().toHuman()); 155 } 156 } 157 } 158 159 @Override 160 public final void popArgs(Frame frame, Type type) { 161 // Use the above method to do the actual popping... 162 popArgs(frame, 1); 163 164 // ...and then verify the popped type. 165 if (! Merger.isPossiblyAssignableFrom(type, args[0])) { 166 throw new SimException("expected type " + type.toHuman() + 167 " but found " + args[0].getType().toHuman()); 168 } 169 } 170 171 /** {@inheritDoc} */ 172 @Override 173 public final void popArgs(Frame frame, Type type1, Type type2) { 174 // Use the above method to do the actual popping... 175 popArgs(frame, 2); 176 177 // ...and then verify the popped types. 178 179 if (! Merger.isPossiblyAssignableFrom(type1, args[0])) { 180 throw new SimException("expected type " + type1.toHuman() + 181 " but found " + args[0].getType().toHuman()); 182 } 183 184 if (! Merger.isPossiblyAssignableFrom(type2, args[1])) { 185 throw new SimException("expected type " + type2.toHuman() + 186 " but found " + args[1].getType().toHuman()); 187 } 188 } 189 190 /** {@inheritDoc} */ 191 @Override 192 public final void popArgs(Frame frame, Type type1, Type type2, 193 Type type3) { 194 // Use the above method to do the actual popping... 195 popArgs(frame, 3); 196 197 // ...and then verify the popped types. 198 199 if (! Merger.isPossiblyAssignableFrom(type1, args[0])) { 200 throw new SimException("expected type " + type1.toHuman() + 201 " but found " + args[0].getType().toHuman()); 202 } 203 204 if (! Merger.isPossiblyAssignableFrom(type2, args[1])) { 205 throw new SimException("expected type " + type2.toHuman() + 206 " but found " + args[1].getType().toHuman()); 207 } 208 209 if (! Merger.isPossiblyAssignableFrom(type3, args[2])) { 210 throw new SimException("expected type " + type3.toHuman() + 211 " but found " + args[2].getType().toHuman()); 212 } 213 } 214 215 /** {@inheritDoc} */ 216 @Override 217 public final void localArg(Frame frame, int idx) { 218 clearArgs(); 219 args[0] = frame.getLocals().get(idx); 220 argCount = 1; 221 localIndex = idx; 222 } 223 224 /** {@inheritDoc} */ 225 @Override 226 public final void localInfo(boolean local) { 227 localInfo = local; 228 } 229 230 /** {@inheritDoc} */ 231 @Override 232 public final void auxType(Type type) { 233 auxType = type; 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public final void auxIntArg(int value) { 239 auxInt = value; 240 } 241 242 /** {@inheritDoc} */ 243 @Override 244 public final void auxCstArg(Constant cst) { 245 if (cst == null) { 246 throw new NullPointerException("cst == null"); 247 } 248 249 auxCst = cst; 250 } 251 252 /** {@inheritDoc} */ 253 @Override 254 public final void auxTargetArg(int target) { 255 auxTarget = target; 256 } 257 258 /** {@inheritDoc} */ 259 @Override 260 public final void auxSwitchArg(SwitchList cases) { 261 if (cases == null) { 262 throw new NullPointerException("cases == null"); 263 } 264 265 auxCases = cases; 266 } 267 268 /** {@inheritDoc} */ 269 @Override 270 public final void auxInitValues(ArrayList<Constant> initValues) { 271 auxInitValues = initValues; 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public final void localTarget(int idx, Type type, LocalItem local) { 277 localTarget = RegisterSpec.makeLocalOptional(idx, type, local); 278 } 279 280 /** 281 * Gets the number of primary arguments. 282 * 283 * @return {@code >= 0;} the number of primary arguments 284 */ 285 protected final int argCount() { 286 return argCount; 287 } 288 289 /** 290 * Gets the width of the arguments (where a category-2 value counts as 291 * two). 292 * 293 * @return {@code >= 0;} the argument width 294 */ 295 protected final int argWidth() { 296 int result = 0; 297 298 for (int i = 0; i < argCount; i++) { 299 result += args[i].getType().getCategory(); 300 } 301 302 return result; 303 } 304 305 /** 306 * Gets the {@code n}th primary argument. 307 * 308 * @param n {@code >= 0, < argCount();} which argument 309 * @return {@code non-null;} the indicated argument 310 */ 311 protected final TypeBearer arg(int n) { 312 if (n >= argCount) { 313 throw new IllegalArgumentException("n >= argCount"); 314 } 315 316 try { 317 return args[n]; 318 } catch (ArrayIndexOutOfBoundsException ex) { 319 // Translate the exception. 320 throw new IllegalArgumentException("n < 0"); 321 } 322 } 323 324 /** 325 * Gets the type auxiliary argument. 326 * 327 * @return {@code null-ok;} the salient type 328 */ 329 protected final Type getAuxType() { 330 return auxType; 331 } 332 333 /** 334 * Gets the {@code int} auxiliary argument. 335 * 336 * @return the argument value 337 */ 338 protected final int getAuxInt() { 339 return auxInt; 340 } 341 342 /** 343 * Gets the constant auxiliary argument. 344 * 345 * @return {@code null-ok;} the argument value 346 */ 347 protected final Constant getAuxCst() { 348 return auxCst; 349 } 350 351 /** 352 * Gets the branch target auxiliary argument. 353 * 354 * @return the argument value 355 */ 356 protected final int getAuxTarget() { 357 return auxTarget; 358 } 359 360 /** 361 * Gets the switch cases auxiliary argument. 362 * 363 * @return {@code null-ok;} the argument value 364 */ 365 protected final SwitchList getAuxCases() { 366 return auxCases; 367 } 368 369 /** 370 * Gets the init values auxiliary argument. 371 * 372 * @return {@code null-ok;} the argument value 373 */ 374 protected final ArrayList<Constant> getInitValues() { 375 return auxInitValues; 376 } 377 /** 378 * Gets the last local index accessed. 379 * 380 * @return {@code >= -1;} the salient local index or {@code -1} if none 381 * was set since the last time {@link #clearArgs} was called 382 */ 383 protected final int getLocalIndex() { 384 return localIndex; 385 } 386 387 /** 388 * Gets whether the loaded local has info in the local variable table. 389 * 390 * @return {@code true} if local arg has info in the local variable table 391 */ 392 protected final boolean getLocalInfo() { 393 return localInfo; 394 } 395 396 /** 397 * Gets the target local register spec of the current operation, if any. 398 * The local target spec is the combination of the values indicated 399 * by a previous call to {@link #localTarget} with the type of what 400 * should be the sole result set by a call to {@link #setResult} (or 401 * the combination {@link #clearResult} then {@link #addResult}. 402 * 403 * @param isMove {@code true} if the operation being performed on the 404 * local is a move. This will cause constant values to be propagated 405 * to the returned local 406 * @return {@code null-ok;} the salient register spec or {@code null} if no 407 * local target was set since the last time {@link #clearArgs} was 408 * called 409 */ 410 protected final RegisterSpec getLocalTarget(boolean isMove) { 411 if (localTarget == null) { 412 return null; 413 } 414 415 if (resultCount != 1) { 416 throw new SimException("local target with " + 417 ((resultCount == 0) ? "no" : "multiple") + " results"); 418 } 419 420 TypeBearer result = results[0]; 421 Type resultType = result.getType(); 422 Type localType = localTarget.getType(); 423 424 if (resultType == localType) { 425 /* 426 * If this is to be a move operation and the result is a 427 * known value, make the returned localTarget embody that 428 * value. 429 */ 430 if (isMove) { 431 return localTarget.withType(result); 432 } else { 433 return localTarget; 434 } 435 } 436 437 if (! Merger.isPossiblyAssignableFrom(localType, resultType)) { 438 // The result and local types are inconsistent. Complain! 439 throwLocalMismatch(resultType, localType); 440 return null; 441 } 442 443 if (localType == Type.OBJECT) { 444 /* 445 * The result type is more specific than the local type, 446 * so use that instead. 447 */ 448 localTarget = localTarget.withType(result); 449 } 450 451 return localTarget; 452 } 453 454 /** 455 * Clears the results. 456 */ 457 protected final void clearResult() { 458 resultCount = 0; 459 } 460 461 /** 462 * Sets the results list to be the given single value. 463 * 464 * <p><b>Note:</b> If there is more than one result value, the 465 * others may be added by using {@link #addResult}.</p> 466 * 467 * @param result {@code non-null;} result value 468 */ 469 protected final void setResult(TypeBearer result) { 470 if (result == null) { 471 throw new NullPointerException("result == null"); 472 } 473 474 results[0] = result; 475 resultCount = 1; 476 } 477 478 /** 479 * Adds an additional element to the list of results. 480 * 481 * @see #setResult 482 * 483 * @param result {@code non-null;} result value 484 */ 485 protected final void addResult(TypeBearer result) { 486 if (result == null) { 487 throw new NullPointerException("result == null"); 488 } 489 490 results[resultCount] = result; 491 resultCount++; 492 } 493 494 /** 495 * Gets the count of results. This throws an exception if results were 496 * never set. (Explicitly clearing the results counts as setting them.) 497 * 498 * @return {@code >= 0;} the count 499 */ 500 protected final int resultCount() { 501 if (resultCount < 0) { 502 throw new SimException("results never set"); 503 } 504 505 return resultCount; 506 } 507 508 /** 509 * Gets the width of the results (where a category-2 value counts as 510 * two). 511 * 512 * @return {@code >= 0;} the result width 513 */ 514 protected final int resultWidth() { 515 int width = 0; 516 517 for (int i = 0; i < resultCount; i++) { 518 width += results[i].getType().getCategory(); 519 } 520 521 return width; 522 } 523 524 /** 525 * Gets the {@code n}th result value. 526 * 527 * @param n {@code >= 0, < resultCount();} which result 528 * @return {@code non-null;} the indicated result value 529 */ 530 protected final TypeBearer result(int n) { 531 if (n >= resultCount) { 532 throw new IllegalArgumentException("n >= resultCount"); 533 } 534 535 try { 536 return results[n]; 537 } catch (ArrayIndexOutOfBoundsException ex) { 538 // Translate the exception. 539 throw new IllegalArgumentException("n < 0"); 540 } 541 } 542 543 /** 544 * Stores the results of the latest operation into the given frame. If 545 * there is a local target (see {@link #localTarget}), then the sole 546 * result is stored to that target; otherwise any results are pushed 547 * onto the stack. 548 * 549 * @param frame {@code non-null;} frame to operate on 550 */ 551 protected final void storeResults(Frame frame) { 552 if (resultCount < 0) { 553 throw new SimException("results never set"); 554 } 555 556 if (resultCount == 0) { 557 // Nothing to do. 558 return; 559 } 560 561 if (localTarget != null) { 562 /* 563 * Note: getLocalTarget() doesn't necessarily return 564 * localTarget directly. 565 */ 566 frame.getLocals().set(getLocalTarget(false)); 567 } else { 568 ExecutionStack stack = frame.getStack(); 569 for (int i = 0; i < resultCount; i++) { 570 if (localInfo) { 571 stack.setLocal(); 572 } 573 stack.push(results[i]); 574 } 575 } 576 } 577 578 /** 579 * Throws an exception that indicates a mismatch in local variable 580 * types. 581 * 582 * @param found {@code non-null;} the encountered type 583 * @param local {@code non-null;} the local variable's claimed type 584 */ 585 public static void throwLocalMismatch(TypeBearer found, 586 TypeBearer local) { 587 throw new SimException("local variable type mismatch: " + 588 "attempt to set or access a value of type " + 589 found.toHuman() + 590 " using a local variable of type " + 591 local.toHuman() + 592 ". This is symptomatic of .class transformation tools " + 593 "that ignore local variable information."); 594 } 595 } 596