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