< prev index next > src/java.base/share/classes/jdk/internal/vm/VMSupport.java
Print this page
/*
! * 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
/*
! * 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
* 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.
*/
* 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.
*/
* 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;
- }
}
< prev index next >