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