/
niceSOFT
/
openjdk21
Обзор
Документация
Войти
/
niceSOFT
/
openjdk21
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/jdk/internal/vm/VMSupport.java
593 строки
25 KB
Tobias Hartmann
8310829: guarantee(!HAS_PENDING_EXCEPTION) failed in ExceptionTranslation::doit
03 июл 2023, 13:37
03 июл 2023, 13:37
6de4e8f
Код
Авторство
О чём код?
/* * Copyright (c) 2005, 2022, 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * 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. */ public static synchronized Properties getAgentProperties() { if (agentProps == null) { agentProps = new Properties(); initAgentProperties(agentProps); } return agentProps; } private static native Properties initAgentProperties(Properties props); /** * Writes the given properties list to a byte array and return it. The stream written * to the byte array is ISO 8859-1 encoded. */ private static byte[] serializePropertiesToByteArray(Properties p) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(4096); p.store(out, null); return out.toByteArray(); } /** * @return a Properties object containing only the entries in {@code p} * whose key and value are both Strings */ private static Properties onlyStrings(Properties p) { Properties props = new Properties(); // stringPropertyNames() returns a snapshot of the property keys Set<String> keyset = p.stringPropertyNames(); for (String key : keyset) { String value = p.getProperty(key); props.put(key, value); } return props; } public static byte[] serializePropertiesToByteArray() throws IOException { return serializePropertiesToByteArray(onlyStrings(System.getProperties())); } public static byte[] serializeAgentPropertiesToByteArray() throws IOException { return serializePropertiesToByteArray(onlyStrings(getAgentProperties())); } /** * Serializes {@link VM#getSavedProperties()} to a byte array. * * Used by JVMCI to copy properties into libjvmci. */ public static byte[] serializeSavedPropertiesToByteArray() throws IOException { Properties props = new Properties(); for (var e : VM.getSavedProperties().entrySet()) { props.put(e.getKey(), e.getValue()); } return serializePropertiesToByteArray(props); } /* * Return the temporary directory that the VM uses for the attach * and perf data files. * * 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 * </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 */ public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap) throws Throwable { if (format != 0) { 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("OutOfMemoryError occurred " + 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)); } 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({"rawtypes", "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; } }