1 /* 2 * Copyright (C) 2016 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 dalvik.system; 18 19 import sun.invoke.util.Wrapper; 20 21 import java.lang.invoke.MethodType; 22 import java.nio.ByteBuffer; 23 import java.nio.ByteOrder; 24 25 /** 26 * Provides typed (read-only) access to method arguments and a slot to store a return value. 27 * 28 * Used to implement method handle transforms. See {@link java.lang.invoke.Transformers}. 29 * 30 * @hide 31 */ 32 public class EmulatedStackFrame { 33 /** 34 * The type of this stack frame, i.e, the types of its arguments and the type of its 35 * return value. 36 */ 37 private final MethodType type; 38 39 /** 40 * All reference arguments and reference return values that belong to this argument array. 41 * 42 * If the return type is a reference, it will be the last element of this array. 43 */ 44 private final Object[] references; 45 46 /** 47 * Contains all primitive values on the stack. Primitive values always take 4 or 8 bytes of 48 * space and all {@code short}, {@code char} and {@code boolean} arguments are promoted to ints. 49 * 50 * Reference values do not appear on the stack frame but they appear (in order) 51 * in the {@code references} array. No additional slots or space for reference arguments or 52 * return values are reserved in the stackFrame. 53 * 54 * By convention, if the return value is a primitive, it will occupy the last 4 or 8 bytes 55 * of the stack frame, depending on the type. 56 * 57 * The size of this array is known at the time of creation of this {@code EmulatedStackFrame} 58 * and is determined by the {@code MethodType} of the frame. 59 * 60 * Example : 61 * <pre> 62 * Function : String foo(String a, String b, int c, long d) { } 63 * 64 * EmulatedStackFrame : 65 * references = { a, b, [return_value] } 66 * stackFrame = { c0, c1, c2, c3, d0, d1, d2, d3, d4, d5, d6, d7 } 67 * 68 * Function : int foo(String a) 69 * 70 * EmulatedStackFrame : 71 * references = { a } 72 * stackFrame = { rv0, rv1, rv2, rv3 } // rv is the return value. 73 * 74 * </pre> 75 * 76 */ 77 private final byte[] stackFrame; 78 EmulatedStackFrame(MethodType type, Object[] references, byte[] stackFrame)79 private EmulatedStackFrame(MethodType type, Object[] references, byte[] stackFrame) { 80 this.type = type; 81 this.references = references; 82 this.stackFrame = stackFrame; 83 } 84 85 /** 86 * Returns the {@code MethodType} that the frame was created for. 87 */ getMethodType()88 public final MethodType getMethodType() { return type; } 89 90 /** 91 * Represents a range of arguments on an {@code EmulatedStackFrame}. 92 * 93 * @hide 94 */ 95 public static final class Range { 96 public final int referencesStart; 97 public final int numReferences; 98 99 public final int stackFrameStart; 100 public final int numBytes; 101 102 private static Range EMPTY_RANGE = new Range(0, 0, 0, 0); 103 Range(int referencesStart, int numReferences, int stackFrameStart, int numBytes)104 private Range(int referencesStart, int numReferences, int stackFrameStart, int numBytes) { 105 this.referencesStart = referencesStart; 106 this.numReferences = numReferences; 107 this.stackFrameStart = stackFrameStart; 108 this.numBytes = numBytes; 109 } 110 111 /** Creates a {@code Range} spanning all arguments. 112 * @param frameType the type of the frame. 113 */ all(MethodType frameType)114 public static Range all(MethodType frameType) { 115 return of(frameType, 0, frameType.parameterCount()); 116 } 117 118 /** Creates a {@code Range} spanning specified arguments. 119 * @param frameType the type of the frame. 120 * @param startArg the first argument in the range to be created. 121 * @param endArg the argument ending the range to be created. 122 */ of(MethodType frameType, int startArg, int endArg)123 public static Range of(MethodType frameType, int startArg, int endArg) { 124 if (startArg >= endArg) { 125 return EMPTY_RANGE; 126 } 127 128 int referencesStart = 0; 129 int numReferences = 0; 130 int stackFrameStart = 0; 131 int numBytes = 0; 132 133 final Class<?>[] ptypes = frameType.ptypes(); 134 for (int i = 0; i < startArg; ++i) { 135 Class<?> cl = ptypes[i]; 136 if (!cl.isPrimitive()) { 137 referencesStart++; 138 } else { 139 stackFrameStart += getSize(cl); 140 } 141 } 142 143 for (int i = startArg; i < endArg; ++i) { 144 Class<?> cl = ptypes[i]; 145 if (!cl.isPrimitive()) { 146 numReferences++; 147 } else { 148 numBytes += getSize(cl); 149 } 150 } 151 152 return new Range(referencesStart, numReferences, stackFrameStart, numBytes); 153 } 154 155 /** Creates a {@code Range} covering all arguments starting from specified position. 156 * @param frameType the type of the frame. 157 * @param startArg the first argument in the range to be created. 158 */ from(MethodType frameType, int startArg)159 public static Range from(MethodType frameType, int startArg) { 160 return of(frameType, startArg, frameType.parameterCount()); 161 } 162 } 163 164 /** 165 * Creates an emulated stack frame for a given {@code MethodType}. 166 */ create(MethodType frameType)167 public static EmulatedStackFrame create(MethodType frameType) { 168 int numRefs = 0; 169 int frameSize = 0; 170 for (Class<?> ptype : frameType.ptypes()) { 171 if (!ptype.isPrimitive()) { 172 numRefs++; 173 } else { 174 frameSize += getSize(ptype); 175 } 176 } 177 178 final Class<?> rtype = frameType.rtype(); 179 if (!rtype.isPrimitive()) { 180 numRefs++; 181 } else { 182 frameSize += getSize(rtype); 183 } 184 185 return new EmulatedStackFrame(frameType, new Object[numRefs], new byte[frameSize]); 186 } 187 188 /** 189 * Convert parameter index to index within references array. 190 */ getReferenceIndex(int parameterIndex)191 int getReferenceIndex(int parameterIndex) { 192 final Class [] ptypes = type.ptypes(); 193 int refIndex = 0; 194 for (int i = 0; i < parameterIndex; ++i) { 195 if (!ptypes[i].isPrimitive()) { 196 refIndex += 1; 197 } 198 } 199 return refIndex; 200 } 201 202 /** 203 * Sets the {@code idx} to {@code reference}. Type checks are performed. 204 */ setReference(int idx, Object reference)205 public void setReference(int idx, Object reference) { 206 final Class<?>[] ptypes = type.ptypes(); 207 if (idx < 0 || idx >= ptypes.length) { 208 throw new IllegalArgumentException("Invalid index: " + idx); 209 } 210 if (reference != null && !ptypes[idx].isInstance(reference)) { 211 throw new IllegalStateException("reference is not of type: " + type.ptypes()[idx]); 212 } 213 int referenceIndex = getReferenceIndex(idx); 214 references[referenceIndex] = reference; 215 } 216 217 /** 218 * Gets the reference at {@code idx}, checking that it's of type {@code referenceType}. 219 */ getReference(int idx, Class<T> referenceType)220 public <T> T getReference(int idx, Class<T> referenceType) { 221 if (referenceType != type.ptypes()[idx]) { 222 throw new IllegalArgumentException("Argument: " + idx + 223 " is of type " + type.ptypes()[idx] + " expected " + referenceType + ""); 224 } 225 int referenceIndex = getReferenceIndex(idx); 226 return (T) references[referenceIndex]; 227 } 228 229 /** 230 * Copies a specified range of arguments, given by {@code fromRange} to a specified 231 * EmulatedStackFrame {@code other}, with references starting at {@code referencesStart} 232 * and primitives starting at {@code primitivesStart}. 233 */ copyRangeTo(EmulatedStackFrame other, Range fromRange, int referencesStart, int primitivesStart)234 public void copyRangeTo(EmulatedStackFrame other, Range fromRange, int referencesStart, 235 int primitivesStart) { 236 if (fromRange.numReferences > 0) { 237 System.arraycopy(references, fromRange.referencesStart, 238 other.references, referencesStart, fromRange.numReferences); 239 } 240 241 if (fromRange.numBytes > 0) { 242 System.arraycopy(stackFrame, fromRange.stackFrameStart, 243 other.stackFrame, primitivesStart, fromRange.numBytes); 244 } 245 } 246 247 /** 248 * Copies the return value from this stack frame to {@code other}. 249 */ copyReturnValueTo(EmulatedStackFrame other)250 public void copyReturnValueTo(EmulatedStackFrame other) { 251 final Class<?> returnType = type.returnType(); 252 if (!returnType.isPrimitive()) { 253 other.references[other.references.length - 1] = references[references.length - 1]; 254 } else if (!is64BitPrimitive(returnType)) { 255 System.arraycopy(stackFrame, stackFrame.length - 4, 256 other.stackFrame, other.stackFrame.length - 4, 4); 257 } else { 258 System.arraycopy(stackFrame, stackFrame.length - 8, 259 other.stackFrame, other.stackFrame.length - 8, 8); 260 } 261 } 262 setReturnValueTo(Object reference)263 public void setReturnValueTo(Object reference) { 264 final Class<?> returnType = type.returnType(); 265 if (returnType.isPrimitive()) { 266 throw new IllegalStateException("return type is not a reference type: " + returnType); 267 } 268 269 if (reference != null && !returnType.isInstance(reference)) { 270 throw new IllegalArgumentException("reference is not of type " + returnType); 271 } 272 273 references[references.length - 1] = reference; 274 } 275 276 /** 277 * Returns true iff. the input {@code type} needs 64 bits (8 bytes) of storage on an 278 * {@code EmulatedStackFrame}. 279 */ is64BitPrimitive(Class<?> type)280 private static boolean is64BitPrimitive(Class<?> type) { 281 return type == double.class || type == long.class; 282 } 283 284 /** 285 * Returns the size (in bytes) occupied by a given primitive type on an 286 * {@code EmulatedStackFrame}. 287 */ getSize(Class<?> type)288 public static int getSize(Class<?> type) { 289 if (!type.isPrimitive()) { 290 throw new IllegalArgumentException("type.isPrimitive() == false: " + type); 291 } 292 293 if (is64BitPrimitive(type)) { 294 return 8; 295 } else { 296 return 4; 297 } 298 } 299 300 /** 301 * Base class for readers and writers to stack frames. 302 * 303 * @hide 304 */ 305 public static class StackFrameAccessor { 306 /** 307 * The current offset into the references array. 308 */ 309 protected int referencesOffset; 310 311 /** 312 * The index of the current argument being processed. For a function of arity N, 313 * values [0, N) correspond to input arguments, and the special index {@code -2} 314 * maps to the return value. All other indices are invalid. 315 */ 316 protected int argumentIdx; 317 318 /** 319 * Wrapper for {@code EmulatedStackFrame.this.stackFrame}. 320 */ 321 protected ByteBuffer frameBuf; 322 323 /** 324 * The number of arguments that this stack frame expects. 325 */ 326 private int numArgs; 327 328 /** 329 * The stack frame we're currently accessing. 330 */ 331 protected EmulatedStackFrame frame; 332 333 /** 334 * The value of {@code argumentIdx} when this accessor's cursor is pointing to the 335 * frame's return value. 336 */ 337 private static final int RETURN_VALUE_IDX = -2; 338 StackFrameAccessor()339 protected StackFrameAccessor() { 340 referencesOffset = 0; 341 argumentIdx = 0; 342 343 frameBuf = null; 344 numArgs = 0; 345 } 346 347 /** 348 * Attaches this accessor to a given {@code EmulatedStackFrame} to read or write 349 * values to it. Also resets all state associated with the current accessor. 350 */ attach(EmulatedStackFrame stackFrame)351 public StackFrameAccessor attach(EmulatedStackFrame stackFrame) { 352 return attach(stackFrame, 0 /* argumentIdx */, 0 /* referencesOffset */, 353 0 /* frameOffset */); 354 } 355 attach(EmulatedStackFrame stackFrame, int argumentIdx, int referencesOffset, int frameOffset)356 public StackFrameAccessor attach(EmulatedStackFrame stackFrame, int argumentIdx, 357 int referencesOffset, int frameOffset) { 358 if (frame != stackFrame) { 359 // Re-initialize storage if not re-attaching to the same stackFrame. 360 frame = stackFrame; 361 frameBuf = ByteBuffer.wrap(frame.stackFrame).order(ByteOrder.LITTLE_ENDIAN); 362 numArgs = frame.type.ptypes().length; 363 } 364 365 frameBuf.position(frameOffset); 366 this.referencesOffset = referencesOffset; 367 this.argumentIdx = argumentIdx; 368 369 return this; 370 } 371 getCurrentArgumentType()372 private Class<?> getCurrentArgumentType() { 373 if (argumentIdx >= numArgs || argumentIdx == (RETURN_VALUE_IDX + 1)) { 374 throw new IllegalArgumentException("Invalid argument index: " + argumentIdx); 375 } 376 return (argumentIdx == RETURN_VALUE_IDX) ? 377 frame.type.rtype() : frame.type.ptypes()[argumentIdx]; 378 } 379 checkAssignable(Class<?> expectedType, Class<?> actualType)380 private static void checkAssignable(Class<?> expectedType, Class<?> actualType) { 381 if (!expectedType.isAssignableFrom(actualType)) { 382 throw new IllegalArgumentException("Incorrect type: " + actualType 383 + ", expected: " + expectedType); 384 } 385 } 386 checkWriteType(Class<?> type)387 protected void checkWriteType(Class<?> type) { 388 checkAssignable(getCurrentArgumentType(), type); 389 } 390 checkReadType(Class<?> expectedType)391 protected void checkReadType(Class<?> expectedType) { 392 checkAssignable(expectedType, getCurrentArgumentType()); 393 } 394 395 /** 396 * Positions the cursor at the return value location, either in the references array 397 * or in the stack frame array. The next put* or next* call will result in a read or 398 * write to the return value. 399 */ makeReturnValueAccessor()400 public void makeReturnValueAccessor() { 401 Class<?> rtype = frame.type.rtype(); 402 argumentIdx = RETURN_VALUE_IDX; 403 404 // Position the cursor appropriately. The return value is either the last element 405 // of the references array, or the last 4 or 8 bytes of the stack frame. 406 if (rtype.isPrimitive()) { 407 frameBuf.position(frameBuf.capacity() - getSize(rtype)); 408 } else { 409 referencesOffset = frame.references.length - 1; 410 } 411 } 412 copyNext( StackFrameReader reader, StackFrameWriter writer, Class<?> type)413 public static void copyNext( 414 StackFrameReader reader, StackFrameWriter writer, Class<?> type) { 415 switch (Wrapper.basicTypeChar(type)) { 416 case 'L': 417 writer.putNextReference(reader.nextReference(type), type); 418 break; 419 case 'Z': 420 writer.putNextBoolean(reader.nextBoolean()); 421 break; 422 case 'B': 423 writer.putNextByte(reader.nextByte()); 424 break; 425 case 'C': 426 writer.putNextChar(reader.nextChar()); 427 break; 428 case 'S': 429 writer.putNextShort(reader.nextShort()); 430 break; 431 case 'I': 432 writer.putNextInt(reader.nextInt()); 433 break; 434 case 'J': 435 writer.putNextLong(reader.nextLong()); 436 break; 437 case 'F': 438 writer.putNextFloat(reader.nextFloat()); 439 break; 440 case 'D': 441 writer.putNextDouble(reader.nextDouble()); 442 break; 443 } 444 } 445 } 446 447 /** 448 * Provides sequential write access to an emulated stack frame. Allows writes to 449 * argument slots as well as return value slots. 450 */ 451 public static class StackFrameWriter extends StackFrameAccessor { putNextByte(byte value)452 public void putNextByte(byte value) { 453 checkWriteType(byte.class); 454 argumentIdx++; 455 frameBuf.putInt(value); 456 } 457 putNextInt(int value)458 public void putNextInt(int value) { 459 checkWriteType(int.class); 460 argumentIdx++; 461 frameBuf.putInt(value); 462 } 463 putNextLong(long value)464 public void putNextLong(long value) { 465 checkWriteType(long.class); 466 argumentIdx++; 467 frameBuf.putLong(value); 468 } 469 putNextChar(char value)470 public void putNextChar(char value) { 471 checkWriteType(char.class); 472 argumentIdx++; 473 frameBuf.putInt((int) value); 474 } 475 putNextBoolean(boolean value)476 public void putNextBoolean(boolean value) { 477 checkWriteType(boolean.class); 478 argumentIdx++; 479 frameBuf.putInt(value ? 1 : 0); 480 } 481 putNextShort(short value)482 public void putNextShort(short value) { 483 checkWriteType(short.class); 484 argumentIdx++; 485 frameBuf.putInt((int) value); 486 } 487 putNextFloat(float value)488 public void putNextFloat(float value) { 489 checkWriteType(float.class); 490 argumentIdx++; 491 frameBuf.putFloat(value); 492 } 493 putNextDouble(double value)494 public void putNextDouble(double value) { 495 checkWriteType(double.class); 496 argumentIdx++; 497 frameBuf.putDouble(value); 498 } 499 putNextReference(Object value, Class<?> expectedType)500 public void putNextReference(Object value, Class<?> expectedType) { 501 checkWriteType(expectedType); 502 argumentIdx++; 503 frame.references[referencesOffset++] = value; 504 } 505 } 506 507 /** 508 * Provides sequential read access to an emulated stack frame. Allows reads to 509 * argument slots as well as to return value slots. 510 */ 511 public static class StackFrameReader extends StackFrameAccessor { nextByte()512 public byte nextByte() { 513 checkReadType(byte.class); 514 argumentIdx++; 515 return (byte) frameBuf.getInt(); 516 } 517 nextInt()518 public int nextInt() { 519 checkReadType(int.class); 520 argumentIdx++; 521 return frameBuf.getInt(); 522 } 523 nextLong()524 public long nextLong() { 525 checkReadType(long.class); 526 argumentIdx++; 527 return frameBuf.getLong(); 528 } 529 nextChar()530 public char nextChar() { 531 checkReadType(char.class); 532 argumentIdx++; 533 return (char) frameBuf.getInt(); 534 } 535 nextBoolean()536 public boolean nextBoolean() { 537 checkReadType(boolean.class); 538 argumentIdx++; 539 return (frameBuf.getInt() != 0); 540 } 541 nextShort()542 public short nextShort() { 543 checkReadType(short.class); 544 argumentIdx++; 545 return (short) frameBuf.getInt(); 546 } 547 nextFloat()548 public float nextFloat() { 549 checkReadType(float.class); 550 argumentIdx++; 551 return frameBuf.getFloat(); 552 } 553 nextDouble()554 public double nextDouble() { 555 checkReadType(double.class); 556 argumentIdx++; 557 return frameBuf.getDouble(); 558 } 559 nextReference(Class<T> expectedType)560 public <T> T nextReference(Class<T> expectedType) { 561 checkReadType(expectedType); 562 argumentIdx++; 563 return (T) frame.references[referencesOffset++]; 564 } 565 } 566 567 /** 568 * Provides sequential and non-sequential read access to an emulated stack frame. Allows reads 569 * to argument slots as well as to return value slots. 570 */ 571 public static class RandomOrderStackFrameReader extends StackFrameReader { 572 int [] frameOffsets; 573 int [] referencesOffsets; 574 buildTables(MethodType methodType)575 private void buildTables(MethodType methodType) { 576 final Class<?> [] ptypes = methodType.parameterArray(); 577 frameOffsets = new int [ptypes.length]; 578 referencesOffsets = new int [ptypes.length]; 579 int frameOffset = 0; 580 int referenceOffset = 0; 581 for (int i = 0; i < ptypes.length; ++i) { 582 frameOffsets[i] = frameOffset; 583 referencesOffsets[i] = referenceOffset; 584 585 final Class<?> ptype = ptypes[i]; 586 if (ptype.isPrimitive()) { 587 frameOffset += getSize(ptype); 588 } else { 589 referenceOffset += 1; 590 } 591 } 592 } 593 594 @Override attach(EmulatedStackFrame stackFrame, int argumentIdx, int referencesOffset, int frameOffset)595 public StackFrameAccessor attach(EmulatedStackFrame stackFrame, int argumentIdx, 596 int referencesOffset, int frameOffset) { 597 super.attach(stackFrame, argumentIdx, referencesOffset, frameOffset); 598 buildTables(stackFrame.getMethodType()); 599 return this; 600 } 601 602 /** 603 * Position to read argument at specific index. 604 * @param argumentIndex the index of the next argument to be read. 605 * @return this reader. 606 */ moveTo(int argumentIndex)607 public RandomOrderStackFrameReader moveTo(int argumentIndex) { 608 referencesOffset = referencesOffsets[argumentIndex]; 609 frameBuf.position(frameOffsets[argumentIndex]); 610 argumentIdx = argumentIndex; 611 return this; 612 } 613 } 614 } 615