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 89be851dbf5..130a81420b6 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -28,14 +28,19 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.TypeDescriptor; import java.util.stream.Stream; +import jdk.internal.constant.PrimitiveClassDescImpl; +import jdk.internal.constant.ReferenceClassDescImpl; import sun.invoke.util.Wrapper; -import static java.lang.constant.ConstantUtils.binaryToInternal; -import static java.lang.constant.ConstantUtils.dropLastChar; -import static java.lang.constant.ConstantUtils.internalToBinary; -import static java.lang.constant.ConstantUtils.validateMemberName; -import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static jdk.internal.constant.ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS; +import static jdk.internal.constant.ConstantUtils.arrayDepth; +import static jdk.internal.constant.ConstantUtils.binaryToInternal; +import static jdk.internal.constant.ConstantUtils.dropFirstAndLastChar; +import static jdk.internal.constant.ConstantUtils.internalToBinary; +import static jdk.internal.constant.ConstantUtils.validateBinaryClassName; +import static jdk.internal.constant.ConstantUtils.validateInternalClassName; +import static jdk.internal.constant.ConstantUtils.validateMemberName; /** * A nominal descriptor for a @@ -77,7 +82,7 @@ public sealed interface ClassDesc * @see ClassDesc#ofInternalName(String) */ static ClassDesc of(String name) { - ConstantUtils.validateBinaryClassName(requireNonNull(name)); + validateBinaryClassName(name); return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";"); } @@ -103,7 +108,7 @@ public sealed interface ClassDesc * @since 20 */ static ClassDesc ofInternalName(String name) { - ConstantUtils.validateInternalClassName(requireNonNull(name)); + validateInternalClassName(name); return ClassDesc.ofDescriptor("L" + name + ";"); } @@ -122,11 +127,11 @@ public sealed interface ClassDesc * not in the correct format */ static ClassDesc of(String packageName, String className) { - ConstantUtils.validateBinaryClassName(requireNonNull(packageName)); + validateBinaryClassName(packageName); if (packageName.isEmpty()) { return of(className); } - validateMemberName(requireNonNull(className), false); + validateMemberName(className, false); return ofDescriptor("L" + binaryToInternal(packageName) + "/" + className + ";"); } @@ -162,7 +167,7 @@ public sealed interface ClassDesc return (descriptor.length() == 1) ? Wrapper.forPrimitiveType(descriptor.charAt(0)).classDescriptor() // will throw IAE on descriptor.length == 0 or if array dimensions too long - : new ReferenceClassDescImpl(descriptor); + : ReferenceClassDescImpl.of(descriptor); } /** @@ -175,13 +180,18 @@ public sealed interface ClassDesc * @jvms 4.4.1 The CONSTANT_Class_info Structure */ default ClassDesc arrayType() { - int depth = ConstantUtils.arrayDepth(descriptorString()); - if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { + String desc = descriptorString(); + int depth = arrayDepth(desc); + if (depth >= MAX_ARRAY_TYPE_DESC_DIMENSIONS) { throw new IllegalStateException( "Cannot create an array type descriptor with more than " + - ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); + MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); } - return arrayType(1); + String newDesc = "[".concat(desc); + if (desc.length() == 1 && desc.charAt(0) == 'V') { + throw new IllegalArgumentException("not a valid reference type descriptor: " + newDesc); + } + return ReferenceClassDescImpl.ofValidated(newDesc); } /** @@ -196,22 +206,22 @@ public sealed interface ClassDesc * @jvms 4.4.1 The CONSTANT_Class_info Structure */ default ClassDesc arrayType(int rank) { - int netRank; if (rank <= 0) { throw new IllegalArgumentException("rank " + rank + " is not a positive value"); } - try { - int currentDepth = ConstantUtils.arrayDepth(descriptorString()); - netRank = Math.addExact(currentDepth, rank); - if (netRank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { - throw new IllegalArgumentException("rank: " + netRank + - " exceeds maximum supported dimension of " + - ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS); - } - } catch (ArithmeticException ae) { - throw new IllegalArgumentException("Integer overflow in rank computation"); + String desc = descriptorString(); + long currentDepth = arrayDepth(desc); + long netRank = currentDepth + rank; + if (netRank > MAX_ARRAY_TYPE_DESC_DIMENSIONS) { + throw new IllegalArgumentException("rank: " + netRank + + " exceeds maximum supported dimension of " + + MAX_ARRAY_TYPE_DESC_DIMENSIONS); } - return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); + String newDesc = new StringBuilder(desc.length() + rank).repeat('[', rank).append(desc).toString(); + if (desc.length() == 1 && desc.charAt(0) == 'V') { + throw new IllegalArgumentException("not a valid reference type descriptor: " + newDesc); + } + return ReferenceClassDescImpl.ofValidated(newDesc); } /** @@ -235,7 +245,10 @@ public sealed interface ClassDesc validateMemberName(nestedName, false); if (!isClassOrInterface()) throw new IllegalStateException("Outer class is not a class or interface type"); - return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";"); + String desc = descriptorString(); + StringBuilder sb = new StringBuilder(desc.length() + nestedName.length() + 1); + sb.append(desc, 0, desc.length() - 1).append('$').append(nestedName).append(';'); + return ReferenceClassDescImpl.ofValidated(sb.toString()); } /** @@ -255,7 +268,7 @@ public sealed interface ClassDesc if (!isClassOrInterface()) throw new IllegalStateException("Outer class is not a class or interface type"); validateMemberName(firstNestedName, false); - requireNonNull(moreNestedNames); + // implicit null-check for (String addNestedNames : moreNestedNames) { validateMemberName(addNestedNames, false); } @@ -299,7 +312,15 @@ public sealed interface ClassDesc * if this descriptor does not describe an array type */ default ClassDesc componentType() { - return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; + if (isArray()) { + String desc = descriptorString(); + if (desc.length() == 2) { + return Wrapper.forBasicType(desc.charAt(1)).classDescriptor(); + } else { + return ReferenceClassDescImpl.ofValidated(desc.substring(1)); + } + } + return null; } /** @@ -312,9 +333,9 @@ public sealed interface ClassDesc default String packageName() { if (!isClassOrInterface()) return ""; - String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); - int index = className.lastIndexOf('.'); - return (index == -1) ? "" : className.substring(0, index); + String desc = descriptorString(); + int index = desc.lastIndexOf('/'); + return (index == -1) ? "" : internalToBinary(desc.substring(1, index)); } /** @@ -332,11 +353,11 @@ public sealed interface ClassDesc if (isPrimitive()) return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); else if (isClassOrInterface()) { - return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), - descriptorString().length() - 1); + String desc = descriptorString(); + return desc.substring(Math.max(1, desc.lastIndexOf('/') + 1), desc.length() - 1); } else if (isArray()) { - int depth = ConstantUtils.arrayDepth(descriptorString()); + int depth = arrayDepth(descriptorString()); ClassDesc c = this; for (int i=0; inominal descriptor for an diff --git a/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java b/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java index 180841cd798..f89e3f361a1 100644 --- a/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java +++ b/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.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 @@ -39,10 +39,10 @@ import java.util.stream.Stream; import static java.lang.constant.ConstantDescs.CD_Class; import static java.lang.constant.ConstantDescs.CD_VarHandle; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; -import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC; -import static java.lang.constant.ConstantUtils.validateMemberName; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static jdk.internal.constant.ConstantUtils.EMPTY_CONSTANTDESC; +import static jdk.internal.constant.ConstantUtils.validateMemberName; /** * A nominal descriptor for a diff --git a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java index 91d6951153e..616c5b67365 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java +++ b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.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 @@ -28,6 +28,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import jdk.internal.constant.DirectMethodHandleDescImpl; + import static java.lang.constant.ConstantDescs.CD_void; import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR; diff --git a/src/java.base/share/classes/java/lang/constant/MethodTypeDesc.java b/src/java.base/share/classes/java/lang/constant/MethodTypeDesc.java index ff750ccc893..19bce254d49 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodTypeDesc.java +++ b/src/java.base/share/classes/java/lang/constant/MethodTypeDesc.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 @@ -31,6 +31,11 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.constant.ConstantUtils; +import jdk.internal.constant.MethodTypeDescImpl; + +import static java.util.Objects.requireNonNull; + /** * A nominal descriptor for a * {@linkplain MethodType} constant. @@ -64,7 +69,7 @@ public sealed interface MethodTypeDesc * @since 21 */ static MethodTypeDesc of(ClassDesc returnDesc) { - return MethodTypeDescImpl.ofTrusted(returnDesc, ConstantUtils.EMPTY_CLASSDESC); + return MethodTypeDescImpl.ofValidated(requireNonNull(returnDesc), ConstantUtils.EMPTY_CLASSDESC); } /** diff --git a/src/java.base/share/classes/java/lang/constant/ModuleDesc.java b/src/java.base/share/classes/java/lang/constant/ModuleDesc.java index fe68d096f1d..b9690b7d0b3 100644 --- a/src/java.base/share/classes/java/lang/constant/ModuleDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ModuleDesc.java @@ -24,6 +24,9 @@ */ package java.lang.constant; +import jdk.internal.constant.ConstantUtils; +import jdk.internal.constant.ModuleDescImpl; + import static java.util.Objects.requireNonNull; /** diff --git a/src/java.base/share/classes/java/lang/constant/PackageDesc.java b/src/java.base/share/classes/java/lang/constant/PackageDesc.java index 2dc20696a47..c354cafae3c 100644 --- a/src/java.base/share/classes/java/lang/constant/PackageDesc.java +++ b/src/java.base/share/classes/java/lang/constant/PackageDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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,9 @@ */ package java.lang.constant; +import jdk.internal.constant.ConstantUtils; +import jdk.internal.constant.PackageDescImpl; + import static java.util.Objects.requireNonNull; /** diff --git a/src/java.base/share/classes/java/lang/constant/ConstantUtils.java b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java similarity index 84% rename from src/java.base/share/classes/java/lang/constant/ConstantUtils.java rename to src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java index d8a1ccd543b..8d761f4f04e 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantUtils.java +++ b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java @@ -22,28 +22,31 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; import sun.invoke.util.Wrapper; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDesc; +import java.lang.constant.ConstantDescs; import java.util.ArrayList; import java.util.List; import java.util.Set; -import static java.util.Objects.requireNonNull; - /** * Helper methods for the implementation of {@code java.lang.constant}. */ -class ConstantUtils { +public final class ConstantUtils { /** an empty constant descriptor */ public static final ConstantDesc[] EMPTY_CONSTANTDESC = new ConstantDesc[0]; - static final ClassDesc[] EMPTY_CLASSDESC = new ClassDesc[0]; - static final Constable[] EMPTY_CONSTABLE = new Constable[0]; - static final int MAX_ARRAY_TYPE_DESC_DIMENSIONS = 255; + public static final ClassDesc[] EMPTY_CLASSDESC = new ClassDesc[0]; + public static final int MAX_ARRAY_TYPE_DESC_DIMENSIONS = 255; private static final Set pointyNames = Set.of(ConstantDescs.INIT_NAME, ConstantDescs.CLASS_INIT_NAME); + /** No instantiation */ + private ConstantUtils() {} + /** * Validates the correctness of a binary class name. In particular checks for the presence of * invalid characters in the name. @@ -51,9 +54,10 @@ class ConstantUtils { * @param name the class name * @return the class name passed if valid * @throws IllegalArgumentException if the class name is invalid + * @throws NullPointerException if class name is {@code null} */ - static String validateBinaryClassName(String name) { - for (int i=0; i= 0; i--) { + for (int i = name.length() - 1; i >= 0; i--) { char ch = name.charAt(i); if ((ch >= '\u0000' && ch <= '\u001F') || ((ch == '\\' || ch == ':' || ch =='@') && (i == 0 || name.charAt(--i) != '\\'))) @@ -144,11 +149,13 @@ class ConstantUtils { * @param name the name of the member * @return the name passed if valid * @throws IllegalArgumentException if the member name is invalid + * @throws NullPointerException if the member name is {@code null} */ public static String validateMemberName(String name, boolean method) { - if (name.length() == 0) + int len = name.length(); + if (len == 0) throw new IllegalArgumentException("zero-length member name"); - for (int i=0; i parseMethodDescriptor(String descriptor) { + public static List parseMethodDescriptor(String descriptor) { int cur = 0, end = descriptor.length(); ArrayList ptypes = new ArrayList<>(); ptypes.add(null); // placeholder for return type @@ -227,7 +230,8 @@ class ConstantUtils { if (len == 1) { return Wrapper.forPrimitiveType(descriptor.charAt(start)).classDescriptor(); } - return ClassDesc.ofDescriptor(descriptor.substring(start, start + len)); + // Pre-verified in parseMethodDescriptor; avoid redundant verification + return ReferenceClassDescImpl.ofValidated(descriptor.substring(start, start + len)); } private static final char JVM_SIGNATURE_ARRAY = '['; diff --git a/src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/DirectMethodHandleDescImpl.java similarity index 91% rename from src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/DirectMethodHandleDescImpl.java index 47aa3dc36d2..0097ed6b0c2 100644 --- a/src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/DirectMethodHandleDescImpl.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 @@ -22,25 +22,28 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; +import java.lang.constant.ClassDesc; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Objects; import static java.lang.constant.ConstantDescs.CD_void; -import static java.lang.constant.ConstantUtils.validateClassOrInterface; -import static java.lang.constant.ConstantUtils.validateMemberName; import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR; import static java.util.Objects.requireNonNull; +import static jdk.internal.constant.ConstantUtils.validateClassOrInterface; +import static jdk.internal.constant.ConstantUtils.validateMemberName; /** * A nominal descriptor for a direct * {@link MethodHandle}. A {@linkplain DirectMethodHandleDescImpl} corresponds to * a {@code Constant_MethodHandle_info} entry in the constant pool of a classfile. */ -final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc { +public final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc { private final Kind kind; private final ClassDesc owner; @@ -62,7 +65,7 @@ final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc { * is not {@code void} * @jvms 4.2.2 Unqualified Names */ - DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) { + public DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) { if (kind == CONSTRUCTOR) name = ""; @@ -82,12 +85,11 @@ final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc { this.kind = kind; this.owner = owner; this.name = name; - if (kind.isVirtualMethod()) - this.invocationType = type.insertParameterTypes(0, owner); - else if (kind == CONSTRUCTOR) - this.invocationType = type.changeReturnType(owner); - else - this.invocationType = type; + this.invocationType = switch (kind) { + case VIRTUAL, SPECIAL, INTERFACE_VIRTUAL, INTERFACE_SPECIAL -> type.insertParameterTypes(0, owner); + case CONSTRUCTOR -> type.changeReturnType(owner); + default -> type; + }; } private static void validateFieldType(MethodTypeDesc type, boolean isSetter, boolean isVirtual) { diff --git a/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java similarity index 80% rename from src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java index 7ee0995439a..d6d622b419c 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java @@ -22,10 +22,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; import jdk.internal.vm.annotation.Stable; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.security.AccessController; @@ -41,7 +43,7 @@ import static java.util.Objects.requireNonNull; * {@link MethodType}. A {@linkplain MethodTypeDescImpl} corresponds to a * {@code Constant_MethodType_info} entry in the constant pool of a classfile. */ -final class MethodTypeDescImpl implements MethodTypeDesc { +public final class MethodTypeDescImpl implements MethodTypeDesc { private final ClassDesc returnType; private final @Stable ClassDesc[] argTypes; private @Stable String cachedDescriptorString; @@ -65,15 +67,31 @@ final class MethodTypeDescImpl implements MethodTypeDesc { * @param returnType a {@link ClassDesc} describing the return type * @param trustedArgTypes {@link ClassDesc}s describing the trusted parameter types */ - static MethodTypeDescImpl ofTrusted(ClassDesc returnType, ClassDesc[] trustedArgTypes) { + public static MethodTypeDescImpl ofTrusted(ClassDesc returnType, ClassDesc[] trustedArgTypes) { requireNonNull(returnType); - if (trustedArgTypes.length == 0) // implicit null check + // implicit null checks of trustedArgTypes and all elements + for (ClassDesc cd : trustedArgTypes) { + validateArgument(cd); + } + return ofValidated(returnType, trustedArgTypes); + } + + private static ClassDesc validateArgument(ClassDesc arg) { + if (arg.descriptorString().charAt(0) == 'V') // implicit null check + throw new IllegalArgumentException("Void parameters not permitted"); + return arg; + } + + /** + * Constructs a {@linkplain MethodTypeDesc} with the specified pre-validated return type + * and a pre-validated trusted parameter types array. + * + * @param returnType a {@link ClassDesc} describing the return type + * @param trustedArgTypes {@link ClassDesc}s describing the trusted parameter types + */ + public static MethodTypeDescImpl ofValidated(ClassDesc returnType, ClassDesc[] trustedArgTypes) { + if (trustedArgTypes.length == 0) return new MethodTypeDescImpl(returnType, ConstantUtils.EMPTY_CLASSDESC); - - for (ClassDesc cd : trustedArgTypes) - if (cd.descriptorString().charAt(0) == 'V') // implicit null check - throw new IllegalArgumentException("Void parameters not permitted"); - return new MethodTypeDescImpl(returnType, trustedArgTypes); } @@ -86,7 +104,7 @@ final class MethodTypeDescImpl implements MethodTypeDesc { * method descriptor * @jvms 4.3.3 Method Descriptors */ - static MethodTypeDescImpl ofDescriptor(String descriptor) { + public static MethodTypeDescImpl ofDescriptor(String descriptor) { // Implicit null-check of descriptor List ptypes = ConstantUtils.parseMethodDescriptor(descriptor); int args = ptypes.size() - 1; @@ -94,7 +112,7 @@ final class MethodTypeDescImpl implements MethodTypeDesc { ? ptypes.subList(1, args + 1).toArray(ConstantUtils.EMPTY_CLASSDESC) : ConstantUtils.EMPTY_CLASSDESC; - MethodTypeDescImpl result = ofTrusted(ptypes.get(0), paramTypes); + MethodTypeDescImpl result = ofValidated(ptypes.get(0), paramTypes); result.cachedDescriptorString = descriptor; return result; } @@ -127,14 +145,14 @@ final class MethodTypeDescImpl implements MethodTypeDesc { @Override public MethodTypeDesc changeReturnType(ClassDesc returnType) { - return new MethodTypeDescImpl(requireNonNull(returnType), argTypes); + return ofValidated(requireNonNull(returnType), argTypes); } @Override public MethodTypeDesc changeParameterType(int index, ClassDesc paramType) { ClassDesc[] newArgs = argTypes.clone(); - newArgs[index] = paramType; - return ofTrusted(returnType, newArgs); + newArgs[index] = validateArgument(paramType); + return ofValidated(returnType, newArgs); } @Override @@ -149,7 +167,7 @@ final class MethodTypeDescImpl implements MethodTypeDesc { if (end < argTypes.length) { System.arraycopy(argTypes, end, newArgs, start, argTypes.length - end); } - return ofTrusted(returnType, newArgs); + return ofValidated(returnType, newArgs); } @Override @@ -162,10 +180,16 @@ final class MethodTypeDescImpl implements MethodTypeDesc { System.arraycopy(argTypes, 0, newArgs, 0, pos); } System.arraycopy(paramTypes, 0, newArgs, pos, paramTypes.length); + int destPos = pos + paramTypes.length; if (pos < argTypes.length) { - System.arraycopy(argTypes, pos, newArgs, pos + paramTypes.length, argTypes.length - pos); + System.arraycopy(argTypes, pos, newArgs, destPos, argTypes.length - pos); } - return ofTrusted(returnType, newArgs); + // Validate after copying to avoid TOCTOU + for (int i = pos; i < destPos; i++) { + validateArgument(newArgs[i]); + } + + return ofValidated(returnType, newArgs); } @Override diff --git a/src/java.base/share/classes/java/lang/constant/ModuleDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/ModuleDescImpl.java similarity index 86% rename from src/java.base/share/classes/java/lang/constant/ModuleDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/ModuleDescImpl.java index 0a01f5b2b73..89310c1331c 100644 --- a/src/java.base/share/classes/java/lang/constant/ModuleDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/ModuleDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -22,13 +22,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; + +import java.lang.constant.ModuleDesc; /* * Implementation of {@code ModuleDesc} * @param name must have been validated */ -record ModuleDescImpl(String name) implements ModuleDesc { +public record ModuleDescImpl(String name) implements ModuleDesc { @Override public String toString() { diff --git a/src/java.base/share/classes/java/lang/constant/PackageDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/PackageDescImpl.java similarity index 85% rename from src/java.base/share/classes/java/lang/constant/PackageDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/PackageDescImpl.java index 1f0077007b8..3ef65b4e4ea 100644 --- a/src/java.base/share/classes/java/lang/constant/PackageDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/PackageDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -22,13 +22,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; + +import java.lang.constant.PackageDesc; /* * Implementation of {@code PackageDesc} * @param internalName must have been validated */ -record PackageDescImpl(String internalName) implements PackageDesc { +public record PackageDescImpl(String internalName) implements PackageDesc { @Override public String toString() { diff --git a/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java similarity index 88% rename from src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java index 0478fbe3bba..123c76a1e9f 100644 --- a/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 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 @@ -22,8 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DynamicConstantDesc; import java.lang.invoke.MethodHandles; import sun.invoke.util.Wrapper; @@ -34,7 +37,7 @@ import static java.util.Objects.requireNonNull; * A nominal descriptor for the class * constant corresponding to a primitive type (e.g., {@code int.class}). */ -final class PrimitiveClassDescImpl +public final class PrimitiveClassDescImpl extends DynamicConstantDesc> implements ClassDesc { private final String descriptor; @@ -49,7 +52,7 @@ final class PrimitiveClassDescImpl * describe a valid primitive type * @jvms 4.3 Descriptors */ - PrimitiveClassDescImpl(String descriptor) { + public PrimitiveClassDescImpl(String descriptor) { super(ConstantDescs.BSM_PRIMITIVE_CLASS, requireNonNull(descriptor), ConstantDescs.CD_Class); if (descriptor.length() != 1 || "VIJCSBFDZ".indexOf(descriptor.charAt(0)) < 0) diff --git a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java similarity index 83% rename from src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java rename to src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java index 5e2aaa98442..e8d73b08097 100644 --- a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.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 @@ -22,21 +22,25 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; +import java.lang.constant.ClassDesc; import java.lang.invoke.MethodHandles; -import static java.lang.constant.ConstantUtils.*; -import static java.util.Objects.requireNonNull; +import static jdk.internal.constant.ConstantUtils.*; /** * A nominal descriptor for a class, * interface, or array type. A {@linkplain ReferenceClassDescImpl} corresponds to a * {@code Constant_Class_info} entry in the constant pool of a classfile. */ -final class ReferenceClassDescImpl implements ClassDesc { +public final class ReferenceClassDescImpl implements ClassDesc { private final String descriptor; + private ReferenceClassDescImpl(String descriptor) { + this.descriptor = descriptor; + } + /** * Creates a {@linkplain ClassDesc} from a descriptor string for a class or * interface type or an array type. @@ -46,12 +50,23 @@ final class ReferenceClassDescImpl implements ClassDesc { * field descriptor string, or does not describe a class or interface type * @jvms 4.3.2 Field Descriptors */ - ReferenceClassDescImpl(String descriptor) { + public static ReferenceClassDescImpl of(String descriptor) { int dLen = descriptor.length(); int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, dLen, false); if (len <= 1 || len != dLen) throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor)); - this.descriptor = descriptor; + return new ReferenceClassDescImpl(descriptor); + } + + /** + * Creates a {@linkplain ClassDesc} from a pre-validated descriptor string + * for a class or interface type or an array type. + * + * @param descriptor a field descriptor string for a class or interface type + * @jvms 4.3.2 Field Descriptors + */ + public static ReferenceClassDescImpl ofValidated(String descriptor) { + return new ReferenceClassDescImpl(descriptor); } @Override diff --git a/test/jdk/java/lang/constant/ClassDescTest.java b/test/jdk/java/lang/constant/ClassDescTest.java index 4c9493721d9..4d98e1283f0 100644 --- a/test/jdk/java/lang/constant/ClassDescTest.java +++ b/test/jdk/java/lang/constant/ClassDescTest.java @@ -237,6 +237,12 @@ public class ClassDescTest extends SymbolicDescTest { testBadArrayRank(ClassDesc.of("Bar")); testArrayRankOverflow(); } + try { + ConstantDescs.CD_void.arrayType(); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // Expected + } } public void testBadClassDescs() { diff --git a/test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java b/test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.java similarity index 94% rename from test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java rename to test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.java index 12ef29273dc..f3e9619a0e1 100644 --- a/test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java +++ b/test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.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 @@ -21,9 +21,8 @@ * questions. */ -package java.lang.constant; +package jdk.internal.constant; -import java.lang.invoke.*; import java.lang.constant.*; import java.util.*; @@ -35,6 +34,7 @@ import static org.testng.Assert.*; * @test * @bug 8303930 * @compile ConstantUtilsTest.java + * @modules java.base/jdk.internal.constant * @run testng ConstantUtilsTest * @summary unit tests for methods of java.lang.constant.ConstantUtils that are not covered by other unit tests */ diff --git a/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java index c74271995d5..c9ce4a6c100 100644 --- a/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java +++ b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescFactories.java @@ -35,6 +35,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; import java.util.concurrent.TimeUnit; /** @@ -60,4 +61,19 @@ public class ClassDescFactories { public ClassDesc ofDescriptor() { return ClassDesc.ofDescriptor(descString); } + + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @Warmup(iterations = 3, time = 2) + @Measurement(iterations = 6, time = 1) + @Fork(1) + @State(Scope.Thread) + public static class ReferenceOnly { + public ClassDesc desc = ConstantDescs.CD_Object; + @Benchmark + public ClassDesc ofNested() { + return desc.nested("Foo"); + } + + } } diff --git a/test/micro/org/openjdk/bench/java/lang/constant/ClassDescMethods.java b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescMethods.java new file mode 100644 index 00000000000..8d9f686b013 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/constant/ClassDescMethods.java @@ -0,0 +1,79 @@ +/* + * 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.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.concurrent.TimeUnit; + +/** + * Test various operations on + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 3, time = 2) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Thread) +public class ClassDescMethods { + + @Benchmark + public String packageName() { + return ConstantDescs.CD_Object.packageName(); + } + + @Benchmark + public String displayName() { + return ConstantDescs.CD_Object.displayName(); + } + + @Benchmark + public void arrayType(Blackhole bh) { + bh.consume(ConstantDescs.CD_Object.arrayType()); + bh.consume(ConstantDescs.CD_boolean.arrayType()); + } + + @Benchmark + public void arrayType1(Blackhole bh) { + bh.consume(ConstantDescs.CD_Object.arrayType(1)); + bh.consume(ConstantDescs.CD_boolean.arrayType(1)); + } + + @Benchmark + public void arrayType2(Blackhole bh) { + bh.consume(ConstantDescs.CD_Object.arrayType(2)); + bh.consume(ConstantDescs.CD_boolean.arrayType(2)); + } +}