< prev index next >

src/java.base/share/classes/jdk/internal/vm/VMSupport.java

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
--- 1,7 ---
  /*
!  * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this

*** 22,40 ***
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package jdk.internal.vm;
  
- import jdk.internal.misc.Unsafe;
- import jdk.internal.misc.VM;
  import jdk.internal.access.SharedSecrets;
- import jdk.internal.access.JavaLangAccess;
- import jdk.internal.reflect.ConstantPool;
- import sun.reflect.annotation.AnnotationParser;
- import sun.reflect.annotation.AnnotationSupport;
- import sun.reflect.annotation.AnnotationType;
  
- import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
  import java.io.IOException;
- import java.lang.annotation.Annotation;
- import java.lang.annotation.IncompleteAnnotationException;
- import java.nio.charset.StandardCharsets;
- import java.util.Collection;
- import java.util.LinkedHashMap;
- import java.util.Map;
  import java.util.Properties;
  import java.util.Set;
- import java.util.List;
  
  /*
!  * Support class used by JVMCI, JVMTI and VM attach mechanism.
   */
  public class VMSupport {
  
-     private static final Unsafe U = Unsafe.getUnsafe();
      private static Properties agentProps = null;
  
      /**
       * Returns the agent properties.
       */
--- 22,22 ---
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package jdk.internal.vm;
  
  import jdk.internal.access.SharedSecrets;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.util.Properties;
  import java.util.Set;
  
  /*
!  * Support class used by JVMTI and VM attach mechanism.
   */
  public class VMSupport {
  
      private static Properties agentProps = null;
  
      /**
       * Returns the agent properties.
       */

*** 114,478 ***
       * It is important that this directory is well-known and the
       * same for all VM instances. It cannot be affected by configuration
       * variables such as java.io.tmpdir.
       */
      public static native String getVMTemporaryDirectory();
- 
-     /**
-      * Decodes the exception described by {@code format} and {@code buffer} and throws it.
-      *
-      * @param format specifies how to interpret {@code buffer}:
-      *            <pre>
-      *             0: {@code buffer} was created by {@link #encodeThrowable}
-      *             1: native memory for {@code buffer} could not be allocated
-      *             2: an OutOfMemoryError was thrown while encoding the exception
-      *             3: some other problem occured while encoding the exception. If {@code buffer != 0},
-      *                it contains a {@code struct { u4 len; char[len] desc}} where {@code desc} describes the problem
-      *             4: an OutOfMemoryError thrown from within VM code on a
-      *                thread that cannot call Java (OOME has no stack trace)
-      *            </pre>
-      * @param buffer encoded info about the exception to throw (depends on {@code format})
-      * @param inJVMHeap [@code true} if executing in the JVM heap, {@code false} otherwise
-      * @param debug specifies whether debug stack traces should be enabled in case of translation failure
-      */
-     public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap, boolean debug) throws Throwable {
-         if (format != 0) {
-             if (format == 4) {
-                 throw new TranslatedException(new OutOfMemoryError("in VM code and current thread cannot call Java"));
-             }
-             String context = String.format("while encoding an exception to translate it %s the JVM heap",
-                     inJVMHeap ? "to" : "from");
-             if (format == 1) {
-                 throw new InternalError("native buffer could not be allocated " + context);
-             }
-             if (format == 2) {
-                 throw new OutOfMemoryError(context);
-             }
-             if (format == 3 && buffer != 0L) {
-                 byte[] bytes = bufferToBytes(buffer);
-                 throw new InternalError("unexpected problem occurred " + context + ": " + new String(bytes, StandardCharsets.UTF_8));
-             }
-             throw new InternalError("unexpected problem occurred " + context);
-         }
-         throw TranslatedException.decodeThrowable(bufferToBytes(buffer), debug);
-     }
- 
-     private static byte[] bufferToBytes(long buffer) {
-         if (buffer == 0) {
-             return null;
-         }
-         int len = U.getInt(buffer);
-         byte[] bytes = new byte[len];
-         U.copyMemory(null, buffer + 4, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
-         return bytes;
-     }
- 
-     /**
-      * If {@code bufferSize} is large enough, encodes {@code throwable} into a byte array and writes
-      * it to {@code buffer}. The encoding in {@code buffer} can be decoded by
-      * {@link #decodeAndThrowThrowable}.
-      *
-      * @param throwable the exception to encode
-      * @param buffer a native byte buffer
-      * @param bufferSize the size of {@code buffer} in bytes
-      * @return the number of bytes written into {@code buffer} if {@code bufferSize} is large
-      *         enough, otherwise {@code -N} where {@code N} is the value {@code bufferSize} needs to
-      *         be to fit the encoding
-      */
-     public static int encodeThrowable(Throwable throwable, long buffer, int bufferSize) {
-         byte[] encoding = TranslatedException.encodeThrowable(throwable);
-         int requiredSize = 4 + encoding.length;
-         if (bufferSize < requiredSize) {
-             return -requiredSize;
-         }
-         U.putInt(buffer, encoding.length);
-         U.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
-         return requiredSize;
-     }
- 
-     /**
-      * Parses {@code rawAnnotations} into a list of {@link Annotation}s and then
-      * serializes them to a byte array with {@link #encodeAnnotations(Collection)}.
-      */
-     public static byte[] encodeAnnotations(byte[] rawAnnotations,
-                                            Class<?> declaringClass,
-                                            ConstantPool cp,
-                                            boolean forClass,
-                                            Class<? extends Annotation>[] selectAnnotationClasses)
-     {
-         for (Class<?> c : selectAnnotationClasses) {
-             if (!c.isAnnotation()) {
-                 throw new IllegalArgumentException(c + " is not an annotation interface");
-             }
-         }
-         Map<Class<? extends Annotation>, Annotation> annotations =
-                 AnnotationParser.parseSelectAnnotations(rawAnnotations, cp, declaringClass, selectAnnotationClasses);
-         if (forClass && annotations.size() != selectAnnotationClasses.length) {
-             Class<?> superClass = declaringClass.getSuperclass();
-             nextSuperClass:
-             while (superClass != null) {
-                 JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-                 Map<Class<? extends Annotation>, Annotation> superAnnotations =
-                     AnnotationParser.parseSelectAnnotations(
-                             jla.getRawClassAnnotations(superClass),
-                             jla.getConstantPool(superClass),
-                             superClass,
-                             selectAnnotationClasses);
- 
-                 for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
-                     Class<? extends Annotation> annotationClass = e.getKey();
-                     if (!annotations.containsKey(annotationClass) && AnnotationType.getInstance(annotationClass).isInherited()) {
-                         if (annotations.isEmpty()) {
-                             // An empty map might be unmodifiable (e.g. Collections.emptyMap()).
-                             annotations = new LinkedHashMap<Class<? extends Annotation>, Annotation>();
-                         }
-                         annotations.put(annotationClass, e.getValue());
-                         if (annotations.size() == selectAnnotationClasses.length) {
-                             break nextSuperClass;
-                         }
-                     }
-                 }
-                 superClass = superClass.getSuperclass();
-             }
-         }
-         return encodeAnnotations(annotations.values());
-     }
- 
-     /**
-      * Encodes annotations to a byte array. The byte array can be decoded with {@link #decodeAnnotations(byte[], AnnotationDecoder)}.
-      */
-     public static byte[] encodeAnnotations(Collection<Annotation> annotations) {
-         try {
-             ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
-             try (DataOutputStream dos = new DataOutputStream(baos)) {
-                 writeLength(dos, annotations.size());
-                 for (Annotation a : annotations) {
-                     encodeAnnotation(dos, a);
-                 }
-             }
-             return baos.toByteArray();
-         } catch (Exception e) {
-             throw new InternalError(e);
-         }
-     }
- 
-     private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws Exception {
-         Class<? extends Annotation> type = a.annotationType();
-         Map<String, Object> values = AnnotationSupport.memberValues(a);
-         dos.writeUTF(type.getName());
-         writeLength(dos, values.size());
-         for (Map.Entry<String, Object> e : values.entrySet()) {
-             Object value = e.getValue();
-             if (value == null) {
-                 // IncompleteAnnotationException
-                 dos.writeByte('x');
-                 dos.writeUTF(new IncompleteAnnotationException(type, e.getKey()).toString());
-                 continue;
-             }
-             Class<?> valueType = value.getClass();
-             dos.writeUTF(e.getKey());
-             if (valueType == Byte.class) {
-                 dos.writeByte('B');
-                 dos.writeByte((byte) value);
-             } else if (valueType == Character.class) {
-                 dos.writeByte('C');
-                 dos.writeChar((char) value);
-             } else if (valueType == Double.class) {
-                 dos.writeByte('D');
-                 dos.writeDouble((double) value);
-             } else if (valueType == Float.class) {
-                 dos.writeByte('F');
-                 dos.writeFloat((float) value);
-             } else if (valueType == Integer.class) {
-                 dos.writeByte('I');
-                 dos.writeInt((int) value);
-             } else if (valueType == Long.class) {
-                 dos.writeByte('J');
-                 dos.writeLong((long) value);
-             } else if (valueType == Short.class) {
-                 dos.writeByte('S');
-                 dos.writeShort((short) value);
-             } else if (valueType == Boolean.class) {
-                 dos.writeByte('Z');
-                 dos.writeBoolean((boolean) value);
-             } else if (valueType == String.class) {
-                 dos.writeByte('s');
-                 dos.writeUTF((String) value);
-             } else if (valueType == Class.class) {
-                 dos.writeByte('c');
-                 dos.writeUTF(((Class<?>) value).getName());
-             } else if (valueType.isEnum()) {
-                 dos.writeByte('e');
-                 dos.writeUTF(valueType.getName());
-                 dos.writeUTF(((Enum<?>) value).name());
-             } else if (value instanceof Annotation) {
-                 dos.writeByte('@');
-                 encodeAnnotation(dos, (Annotation) value);
-             } else if (valueType.isArray()) {
-                 Class<?> componentType = valueType.getComponentType();
-                 if (componentType == byte.class) {
-                     byte[] array = (byte[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('B');
-                     writeLength(dos, array.length);
-                     dos.write(array);
-                 } else if (componentType == char.class) {
-                     char[] array = (char[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('C');
-                     writeLength(dos, array.length);
-                     for (char c : array) {
-                         dos.writeChar(c);
-                     }
-                 } else if (componentType == double.class) {
-                     double[] array = (double[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('D');
-                     writeLength(dos, array.length);
-                     for (double v : array) {
-                         dos.writeDouble(v);
-                     }
-                 } else if (componentType == float.class) {
-                     float[] array = (float[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('F');
-                     writeLength(dos, array.length);
-                     for (float v : array) {
-                         dos.writeFloat(v);
-                     }
-                 } else if (componentType == int.class) {
-                     int[] array = (int[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('I');
-                     writeLength(dos, array.length);
-                     for (int j : array) {
-                         dos.writeInt(j);
-                     }
-                 } else if (componentType == long.class) {
-                     long[] array = (long[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('J');
-                     writeLength(dos, array.length);
-                     for (long l : array) {
-                         dos.writeLong(l);
-                     }
-                 } else if (componentType == short.class) {
-                     short[] array = (short[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('S');
-                     writeLength(dos, array.length);
-                     for (short item : array) {
-                         dos.writeShort(item);
-                     }
-                 } else if (componentType == boolean.class) {
-                     boolean[] array = (boolean[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('Z');
-                     writeLength(dos, array.length);
-                     for (boolean b : array) {
-                         dos.writeBoolean(b);
-                     }
-                 } else if (componentType == String.class) {
-                     String[] array = (String[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('s');
-                     writeLength(dos, array.length);
-                     for (String s : array) {
-                         dos.writeUTF(s);
-                     }
-                 } else if (componentType == Class.class) {
-                     Class<?>[] array = (Class<?>[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('c');
-                     writeLength(dos, array.length);
-                     for (Class<?> aClass : array) {
-                         dos.writeUTF(aClass.getName());
-                     }
-                 } else if (componentType.isEnum()) {
-                     Enum<?>[] array = (Enum<?>[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('e');
-                     dos.writeUTF(componentType.getName());
-                     writeLength(dos, array.length);
-                     for (Enum<?> anEnum : array) {
-                         dos.writeUTF(anEnum.name());
-                     }
-                 } else if (componentType.isAnnotation()) {
-                     Annotation[] array = (Annotation[]) value;
-                     dos.writeByte('[');
-                     dos.writeByte('@');
-                     writeLength(dos, array.length);
-                     for (Annotation annotation : array) {
-                         encodeAnnotation(dos, annotation);
-                     }
-                 } else {
-                     dos.writeByte('x');
-                     dos.writeUTF(value.toString());
-                 }
- 
-             } else {
-                 dos.writeByte('x');
-                 dos.writeUTF(value.toString());
-             }
-         }
-     }
- 
-     /**
-      * Helper for {@link #decodeAnnotations(byte[], AnnotationDecoder)} to convert a byte
-      * array (ostensibly produced by {@link VMSupport#encodeAnnotations}) into objects.
-      *
-      * @param <T> type to which a type name is {@linkplain #resolveType(String) resolved}
-      * @param <A> type of the object representing a decoded annotation
-      * @param <E> type of the object representing a decoded enum constant
-      * @param <X> type of the object representing a decoded error
-      */
-     public interface AnnotationDecoder<T, A, E, X> {
-         /**
-          * Resolves a name in {@link Class#getName()} format to an object of type {@code T}.
-          */
-         T resolveType(String name);
- 
-         /**
-          * Creates an object representing a decoded annotation.
-          *
-          * @param type the annotation interface of the annotation
-          * @param elements elements of the annotation
-          */
-         A newAnnotation(T type, Map.Entry<String, Object>[] elements);
- 
-         /**
-          * Creates an object representing a decoded enum constant.
-          *
-          * @param enumType the enum type
-          * @param name the name of the enum constant
-          */
-         E newEnumValue(T enumType, String name);
- 
-         /**
-          * Creates an object representing a decoded error value.
-          *
-          * @param description of the error
-          */
-         X newErrorValue(String description);
-     }
- 
-     /**
-      * Decodes annotations serialized in {@code encoded} to objects.
-      *
-      * @param <T> type to which a type name is resolved
-      * @param <A> type of the object representing a decoded annotation
-      * @param <E> type of the object representing a decoded enum constant
-      * @param <X> type of the object representing a decoded error
-      * @return an immutable list of {@code A} objects
-      */
-     @SuppressWarnings("unchecked")
-     public static <T, A, E, X> List<A> decodeAnnotations(byte[] encoded, AnnotationDecoder<T, A, E, X> decoder) {
-         try {
-             ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
-             DataInputStream dis = new DataInputStream(bais);
-             return (List<A>) readArray(dis, () -> decodeAnnotation(dis, decoder));
-         } catch (Exception e) {
-             throw new InternalError(e);
-         }
-     }
- 
-     @SuppressWarnings({"rawtypes", "unchecked"})
-     private static <T, A, E, X> A decodeAnnotation(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
-         String typeName = dis.readUTF();
-         T type = decoder.resolveType(typeName);
-         int n = readLength(dis);
-         Map.Entry[] elements = new Map.Entry[n];
-         for (int i = 0; i < n; i++) {
-             String name = dis.readUTF();
-             byte tag = dis.readByte();
-             elements[i] = Map.entry(name, switch (tag) {
-                 case 'B' -> dis.readByte();
-                 case 'C' -> dis.readChar();
-                 case 'D' -> dis.readDouble();
-                 case 'F' -> dis.readFloat();
-                 case 'I' -> dis.readInt();
-                 case 'J' -> dis.readLong();
-                 case 'S' -> dis.readShort();
-                 case 'Z' -> dis.readBoolean();
-                 case 's' -> dis.readUTF();
-                 case 'c' -> decoder.resolveType(dis.readUTF());
-                 case 'e' -> decoder.newEnumValue(decoder.resolveType(dis.readUTF()), dis.readUTF());
-                 case '@' -> decodeAnnotation(dis, decoder);
-                 case '[' -> decodeArray(dis, decoder);
-                 case 'x' -> decoder.newErrorValue(dis.readUTF());
-                 default -> throw new InternalError("Unsupported tag: " + tag);
-             });
-         }
-         return decoder.newAnnotation(type, (Map.Entry<String, Object>[]) elements);
-     }
-     @FunctionalInterface
-     interface IOReader {
-         Object read() throws IOException;
-     }
- 
-     private static <T, A, E, X> Object decodeArray(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
-         byte componentTag = dis.readByte();
-         return switch (componentTag) {
-             case 'B' -> readArray(dis, dis::readByte);
-             case 'C' -> readArray(dis, dis::readChar);
-             case 'D' -> readArray(dis, dis::readDouble);
-             case 'F' -> readArray(dis, dis::readFloat);
-             case 'I' -> readArray(dis, dis::readInt);
-             case 'J' -> readArray(dis, dis::readLong);
-             case 'S' -> readArray(dis, dis::readShort);
-             case 'Z' -> readArray(dis, dis::readBoolean);
-             case 's' -> readArray(dis, dis::readUTF);
-             case 'c' -> readArray(dis, () -> readClass(dis, decoder));
-             case 'e' -> {
-                 T enumType = decoder.resolveType(dis.readUTF());
-                 yield readArray(dis, () -> readEnum(dis, decoder, enumType));
-             }
-             case '@' -> readArray(dis, () -> decodeAnnotation(dis, decoder));
-             default -> throw new InternalError("Unsupported component tag: " + componentTag);
-         };
-     }
- 
-     /**
-      * Reads an enum encoded at the current read position of {@code dis} and
-      * returns it as an object of type {@code E}.
-      */
-     private static <T, A, E, X> E readEnum(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder, T enumType) throws IOException {
-         return decoder.newEnumValue(enumType, dis.readUTF());
-     }
- 
-     /**
-      * Reads a class encoded at the current read position of {@code dis} and
-      * returns it as an object of type {@code T}.
-      */
-     private static <T, A, E, X> T readClass(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
-         return decoder.resolveType(dis.readUTF());
-     }
- 
-     /**
-      * Reads an array encoded at the current read position of {@code dis} and
-      * returns it in an immutable list.
-      *
-      * @param reader reads array elements from {@code dis}
-      * @return an immutable list of {@code A} objects
-      */
-     private static List<Object> readArray(DataInputStream dis, IOReader reader) throws IOException {
-         Object[] array = new Object[readLength(dis)];
-         for (int i = 0; i < array.length; i++) {
-             array[i] = reader.read();
-         }
-         return List.of(array);
-     }
- 
-     /**
-      * Encodes {@code length} in 1 byte if it is less than 128.
-      */
-     private static void writeLength(DataOutputStream dos, int length) throws IOException {
-         if (length < 0) {
-             throw new NegativeArraySizeException();
-         } else if (length <= 127) {
-             dos.writeByte((byte) (0x80 | length));
-         } else {
-             dos.writeInt(length);
-         }
-     }
- 
-     private static int readLength(DataInputStream dis) throws IOException {
-         int ch1 = dis.readByte();
-         int length;
-         if (ch1 < 0) {
-             length = ch1 & 0x7F;
-         } else {
-             int ch2 = dis.read();
-             int ch3 = dis.read();
-             int ch4 = dis.read();
-             length = (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
-         }
-         return length;
-     }
  }
--- 96,6 ---
< prev index next >