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