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 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 */ 94 public final MethodType getMethodType() { return type; } 95 96 /** 97 * Returns the {@code MethodType} corresponding to the callsite of the 98 */ 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 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 120 public static Range all(MethodType frameType) { 121 return of(frameType, 0, frameType.parameterCount()); 122 } 123 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 329 public StackFrameAccessor attach(EmulatedStackFrame stackFrame) { 330 return attach(stackFrame, 0 /* argumentIdx */, 0 /* referencesOffset */, 331 0 /* frameOffset */); 332 } 333 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 349 protected void checkType(Class<?> type) { 350 if (argumentIdx >= numArgs || argumentIdx == (RETURN_VALUE_IDX + 1)) { 351 throw new IllegalArgumentException("Invalid argument index: " + argumentIdx); 352 } 353 354 final Class<?> expectedType = (argumentIdx == RETURN_VALUE_IDX) ? 355 frame.type.rtype() : frame.type.ptypes()[argumentIdx]; 356 357 if (expectedType != type) { 358 throw new IllegalArgumentException("Incorrect type: " + type + 359 ", expected: " + expectedType); 360 } 361 } 362 363 /** 364 * Positions the cursor at the return value location, either in the references array 365 * or in the stack frame array. The next put* or next* call will result in a read or 366 * write to the return value. 367 */ 368 public void makeReturnValueAccessor() { 369 Class<?> rtype = frame.type.rtype(); 370 argumentIdx = RETURN_VALUE_IDX; 371 372 // Position the cursor appropriately. The return value is either the last element 373 // of the references array, or the last 4 or 8 bytes of the stack frame. 374 if (rtype.isPrimitive()) { 375 frameBuf.position(frameBuf.capacity() - getSize(rtype)); 376 } else { 377 referencesOffset = frame.references.length - 1; 378 } 379 } 380 381 public static void copyNext(StackFrameReader reader, StackFrameWriter writer, 382 Class<?> type) { 383 if (!type.isPrimitive()) { 384 writer.putNextReference(reader.nextReference(type), type); 385 } else if (type == boolean.class) { 386 writer.putNextBoolean(reader.nextBoolean()); 387 } else if (type == byte.class) { 388 writer.putNextByte(reader.nextByte()); 389 } else if (type == char.class) { 390 writer.putNextChar(reader.nextChar()); 391 } else if (type == short.class) { 392 writer.putNextShort(reader.nextShort()); 393 } else if (type == int.class) { 394 writer.putNextInt(reader.nextInt()); 395 } else if (type == long.class) { 396 writer.putNextLong(reader.nextLong()); 397 } else if (type == float.class) { 398 writer.putNextFloat(reader.nextFloat()); 399 } else if (type == double.class) { 400 writer.putNextDouble(reader.nextDouble()); 401 } 402 } 403 } 404 405 /** 406 * Provides sequential write access to an emulated stack frame. Allows writes to 407 * argument slots as well as return value slots. 408 */ 409 public static class StackFrameWriter extends StackFrameAccessor { 410 public void putNextByte(byte value) { 411 checkType(byte.class); 412 argumentIdx++; 413 frameBuf.putInt(value); 414 } 415 416 public void putNextInt(int value) { 417 checkType(int.class); 418 argumentIdx++; 419 frameBuf.putInt(value); 420 } 421 422 public void putNextLong(long value) { 423 checkType(long.class); 424 argumentIdx++; 425 frameBuf.putLong(value); 426 } 427 428 public void putNextChar(char value) { 429 checkType(char.class); 430 argumentIdx++; 431 frameBuf.putInt((int) value); 432 } 433 434 public void putNextBoolean(boolean value) { 435 checkType(boolean.class); 436 argumentIdx++; 437 frameBuf.putInt(value ? 1 : 0); 438 } 439 440 public void putNextShort(short value) { 441 checkType(short.class); 442 argumentIdx++; 443 frameBuf.putInt((int) value); 444 } 445 446 public void putNextFloat(float value) { 447 checkType(float.class); 448 argumentIdx++; 449 frameBuf.putFloat(value); 450 } 451 452 public void putNextDouble(double value) { 453 checkType(double.class); 454 argumentIdx++; 455 frameBuf.putDouble(value); 456 } 457 458 public void putNextReference(Object value, Class<?> expectedType) { 459 checkType(expectedType); 460 argumentIdx++; 461 frame.references[referencesOffset++] = value; 462 } 463 } 464 465 /** 466 * Provides sequential read access to an emulated stack frame. Allows reads to 467 * argument slots as well as to return value slots. 468 */ 469 public static class StackFrameReader extends StackFrameAccessor { 470 public byte nextByte() { 471 checkType(byte.class); 472 argumentIdx++; 473 return (byte) frameBuf.getInt(); 474 } 475 476 public int nextInt() { 477 checkType(int.class); 478 argumentIdx++; 479 return frameBuf.getInt(); 480 } 481 482 public long nextLong() { 483 checkType(long.class); 484 argumentIdx++; 485 return frameBuf.getLong(); 486 } 487 488 public char nextChar() { 489 checkType(char.class); 490 argumentIdx++; 491 return (char) frameBuf.getInt(); 492 } 493 494 public boolean nextBoolean() { 495 checkType(boolean.class); 496 argumentIdx++; 497 return (frameBuf.getInt() != 0); 498 } 499 500 public short nextShort() { 501 checkType(short.class); 502 argumentIdx++; 503 return (short) frameBuf.getInt(); 504 } 505 506 public float nextFloat() { 507 checkType(float.class); 508 argumentIdx++; 509 return frameBuf.getFloat(); 510 } 511 512 public double nextDouble() { 513 checkType(double.class); 514 argumentIdx++; 515 return frameBuf.getDouble(); 516 } 517 518 public <T> T nextReference(Class<T> expectedType) { 519 checkType(expectedType); 520 argumentIdx++; 521 return (T) frame.references[referencesOffset++]; 522 } 523 } 524 } 525