From 8bbd7251a596c6fad1a6675c077deb9fd7c8ff95 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Fri, 26 Apr 2024 14:06:53 +0000 Subject: [PATCH] 8331187: Optimize MethodTypeDesc and ClassDesc.ofDescriptor for primitive types Reviewed-by: jvernee, liach --- .../classes/java/lang/constant/ClassDesc.java | 17 ++--- .../java/lang/constant/ConstantDescs.java | 20 +++--- .../java/lang/constant/ConstantUtils.java | 21 +++++-- .../lang/constant/MethodTypeDescImpl.java | 20 +++--- .../classes/sun/invoke/util/Wrapper.java | 33 ++++++---- .../lang/constant/ClassDescFactories.java | 63 +++++++++++++++++++ 6 files changed, 121 insertions(+), 53 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java diff --git a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java index 60adc7f1ab1..6adba119180 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -158,19 +158,10 @@ public sealed interface ClassDesc * @see ClassDesc#ofInternalName(String) */ static ClassDesc ofDescriptor(String descriptor) { - requireNonNull(descriptor); - if (descriptor.isEmpty()) { - throw new IllegalArgumentException( - "not a valid reference type descriptor: " + descriptor); - } - int depth = ConstantUtils.arrayDepth(descriptor); - if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { - throw new IllegalArgumentException( - "Cannot create an array type descriptor with more than " + - ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); - } + // implicit null-check return (descriptor.length() == 1) - ? new PrimitiveClassDescImpl(descriptor) + ? Wrapper.forPrimitiveType(descriptor.charAt(0)).primitiveClassDescriptor() + // will throw IAE on descriptor.length == 0 or if array dimensions too long : new ReferenceClassDescImpl(descriptor); } diff --git a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java index 82f56b7cbd4..de895fc7af5 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java +++ b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -236,31 +236,31 @@ public final class ConstantDescs { CD_Object, CD_Object); /** {@link ClassDesc} representing the primitive type {@code int} */ - public static final ClassDesc CD_int = ClassDesc.ofDescriptor("I"); + public static final ClassDesc CD_int = new PrimitiveClassDescImpl("I"); /** {@link ClassDesc} representing the primitive type {@code long} */ - public static final ClassDesc CD_long = ClassDesc.ofDescriptor("J"); + public static final ClassDesc CD_long = new PrimitiveClassDescImpl("J"); /** {@link ClassDesc} representing the primitive type {@code float} */ - public static final ClassDesc CD_float = ClassDesc.ofDescriptor("F"); + public static final ClassDesc CD_float = new PrimitiveClassDescImpl("F"); /** {@link ClassDesc} representing the primitive type {@code double} */ - public static final ClassDesc CD_double = ClassDesc.ofDescriptor("D"); + public static final ClassDesc CD_double = new PrimitiveClassDescImpl("D"); /** {@link ClassDesc} representing the primitive type {@code short} */ - public static final ClassDesc CD_short = ClassDesc.ofDescriptor("S"); + public static final ClassDesc CD_short = new PrimitiveClassDescImpl("S"); /** {@link ClassDesc} representing the primitive type {@code byte} */ - public static final ClassDesc CD_byte = ClassDesc.ofDescriptor("B"); + public static final ClassDesc CD_byte = new PrimitiveClassDescImpl("B"); /** {@link ClassDesc} representing the primitive type {@code char} */ - public static final ClassDesc CD_char = ClassDesc.ofDescriptor("C"); + public static final ClassDesc CD_char = new PrimitiveClassDescImpl("C"); /** {@link ClassDesc} representing the primitive type {@code boolean} */ - public static final ClassDesc CD_boolean = ClassDesc.ofDescriptor("Z"); + public static final ClassDesc CD_boolean = new PrimitiveClassDescImpl("Z"); /** {@link ClassDesc} representing the primitive type {@code void} */ - public static final ClassDesc CD_void = ClassDesc.ofDescriptor("V"); + public static final ClassDesc CD_void = new PrimitiveClassDescImpl("V"); /** * {@link MethodHandleDesc} representing {@link MethodHandles#classData(Lookup, String, Class) MethodHandles.classData} diff --git a/src/java.base/share/classes/java/lang/constant/ConstantUtils.java b/src/java.base/share/classes/java/lang/constant/ConstantUtils.java index 10ed2758e3a..312aac68c63 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantUtils.java +++ b/src/java.base/share/classes/java/lang/constant/ConstantUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -24,6 +24,8 @@ */ package java.lang.constant; +import sun.invoke.util.Wrapper; + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -195,10 +197,10 @@ class ConstantUtils { * @return the list of types * @throws IllegalArgumentException if the descriptor string is not valid */ - static List parseMethodDescriptor(String descriptor) { + static List parseMethodDescriptor(String descriptor) { int cur = 0, end = descriptor.length(); - ArrayList ptypes = new ArrayList<>(); - ptypes.add(null); //placeholder for return type + ArrayList ptypes = new ArrayList<>(); + ptypes.add(null); // placeholder for return type if (cur >= end || descriptor.charAt(cur) != '(') throw new IllegalArgumentException("Bad method descriptor: " + descriptor); @@ -208,7 +210,7 @@ class ConstantUtils { int len = skipOverFieldSignature(descriptor, cur, end, false); if (len == 0) throw new IllegalArgumentException("Bad method descriptor: " + descriptor); - ptypes.add(descriptor.substring(cur, cur + len)); + ptypes.add(resolveClassDesc(descriptor, cur, len)); cur += len; } if (cur >= end) @@ -218,10 +220,17 @@ class ConstantUtils { int rLen = skipOverFieldSignature(descriptor, cur, end, true); if (rLen == 0 || cur + rLen != end) throw new IllegalArgumentException("Bad method descriptor: " + descriptor); - ptypes.set(0, descriptor.substring(cur, cur + rLen)); + ptypes.set(0, resolveClassDesc(descriptor, cur, rLen)); return ptypes; } + private static ClassDesc resolveClassDesc(String descriptor, int start, int len) { + if (len == 1) { + return Wrapper.forBasicType(descriptor.charAt(start)).primitiveClassDescriptor(); + } + return ClassDesc.ofDescriptor(descriptor.substring(start, start + len)); + } + private static final char JVM_SIGNATURE_ARRAY = '['; private static final char JVM_SIGNATURE_BYTE = 'B'; private static final char JVM_SIGNATURE_CHAR = 'C'; diff --git a/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java b/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java index c50cf7c58f0..40a9cf8bd60 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java +++ b/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -88,21 +88,19 @@ final class MethodTypeDescImpl implements MethodTypeDesc { * @jvms 4.3.3 Method Descriptors */ static MethodTypeDescImpl ofDescriptor(String descriptor) { - requireNonNull(descriptor); + // Implicit null-check of descriptor + List ptypes = ConstantUtils.parseMethodDescriptor(descriptor); + int args = ptypes.size() - 1; + ClassDesc[] paramTypes = args > 0 + ? ptypes.subList(1, args + 1).toArray(ConstantUtils.EMPTY_CLASSDESC) + : ConstantUtils.EMPTY_CLASSDESC; - List types = ConstantUtils.parseMethodDescriptor(descriptor); - - int paramCount = types.size() - 1; - var paramTypes = paramCount > 0 ? new ClassDesc[paramCount] : ConstantUtils.EMPTY_CLASSDESC; - for (int i = 0; i < paramCount; i++) { - paramTypes[i] = ClassDesc.ofDescriptor(types.get(i + 1)); - } - - MethodTypeDescImpl result = ofTrusted(ClassDesc.ofDescriptor(types.getFirst()), paramTypes); + MethodTypeDescImpl result = ofTrusted(ptypes.get(0), paramTypes); result.cachedDescriptorString = descriptor; return result; } + @Override public ClassDesc returnType() { return returnType; diff --git a/src/java.base/share/classes/sun/invoke/util/Wrapper.java b/src/java.base/share/classes/sun/invoke/util/Wrapper.java index 76aede09487..3a28d63b7c4 100644 --- a/src/java.base/share/classes/sun/invoke/util/Wrapper.java +++ b/src/java.base/share/classes/sun/invoke/util/Wrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, 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 @@ -25,25 +25,27 @@ package sun.invoke.util; -import java.util.Map; import static sun.invoke.util.Wrapper.NumericClasses.*; import jdk.internal.vm.annotation.DontInline; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; + public enum Wrapper { // wrapperType simple primitiveType simple char emptyArray format numericClass superClass - BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0), + BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0, ConstantDescs.CD_boolean), // These must be in the order defined for widening primitive conversions in JLS 5.1.2 // Avoid boxing integral types here to defer initialization of internal caches - BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES), - SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES), - CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES), - INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES), - LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES), - FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES), - DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS), - OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0), + BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES, ConstantDescs.CD_byte), + SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES, ConstantDescs.CD_short), + CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES, ConstantDescs.CD_char), + INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES, ConstantDescs.CD_int), + LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES, ConstantDescs.CD_long), + FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES, ConstantDescs.CD_float), + DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS, ConstantDescs.CD_double), + OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0, ConstantDescs.CD_Object), // VOID must be the last type, since it is "assignable" from any other type: - VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0), + VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0, ConstantDescs.CD_void), ; public static final int COUNT = 10; @@ -58,8 +60,9 @@ public enum Wrapper { private final int superClasses; private final String wrapperSimpleName; private final String primitiveSimpleName; + private final ClassDesc primitiveTypeDesc; - private Wrapper(Class wtype, String wtypeName, Class ptype, String ptypeName, char tchar, Object emptyArray, int format, int numericClass, int superClasses) { + private Wrapper(Class wtype, String wtypeName, Class ptype, String ptypeName, char tchar, Object emptyArray, int format, int numericClass, int superClasses, ClassDesc primitiveTypeDesc) { this.wrapperType = wtype; this.primitiveType = ptype; this.basicTypeChar = tchar; @@ -70,6 +73,7 @@ public enum Wrapper { this.superClasses = superClasses; this.wrapperSimpleName = wtypeName; this.primitiveSimpleName = ptypeName; + this.primitiveTypeDesc = primitiveTypeDesc; } /** For debugging, give the details of this wrapper. */ @@ -376,6 +380,9 @@ public enum Wrapper { } } + /** A nominal descriptor of the primitive type */ + public ClassDesc primitiveClassDescriptor() { return primitiveTypeDesc; } + /** What is the primitive type wrapped by this wrapper? */ public Class primitiveType() { return primitiveType; } diff --git a/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java new file mode 100644 index 00000000000..c74271995d5 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, 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. + * + * 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 org.openjdk.bench.java.lang.constant; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.constant.ClassDesc; +import java.util.concurrent.TimeUnit; + +/** + * Performance of conversion from and to method type descriptor symbols with + * descriptor strings and class descriptor symbols + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 3, time = 2) +@Measurement(iterations = 6, time = 1) +@Fork(1) +@State(Scope.Thread) +public class ClassDescFactories { + + @Param({ + "Ljava/lang/Object;", + "V", + "I" + }) + public String descString; + + @Benchmark + public ClassDesc ofDescriptor() { + return ClassDesc.ofDescriptor(descString); + } +}