diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java b/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java index 1ddd0511d85..43bf1984a00 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java @@ -32,9 +32,8 @@ * including {@link Attribute}, {@link AttributedElement}, {@link AttributeMapper}, and {@link CustomAttribute}, which * do not reside in this package. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading Attributes

* The general way to obtain attributes is through {@link AttributedElement}. In addition to that, many attributes diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java b/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java index 66e72496d3a..0828e04ae22 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java @@ -30,9 +30,8 @@ * {@code class} file format. Constant pool entries are low-level models to faithfully represent the exact structure * of a {@code class} file. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading the constant pool entries

* When read from {@code class} files, the pool entries are lazily inflated; the contents of these entries, besides the diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java b/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java index e732aadf1ec..990731721a8 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -29,9 +29,8 @@ * The {@code java.lang.classfile.instruction} package contains interfaces describing code instructions. * Implementations of these interfaces are immutable. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading of instructions

* Instructions and pseudo-instructions are usually accessed from a {@link CodeModel}, such as {@link CodeModel#forEach diff --git a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java index da9ad7fbf0d..460f6699e7b 100644 --- a/src/java.base/share/classes/java/lang/classfile/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/package-info.java @@ -250,12 +250,6 @@ * the convenience method {@code CodeBuilder.invoke}, which in turn behaves * as if it calls method {@code CodeBuilder.with}. This composing of method calls on the * builder enables the composing of transforms (as described later). - *

- * Unless otherwise noted, passing a {@code null} argument to a constructor - * or method of any Class-File API class or interface will cause a {@link - * NullPointerException} to be thrown. Additionally, - * invoking a method with an array or collection containing a {@code null} element - * will cause a {@code NullPointerException}, unless otherwise specified. * *

Symbolic information

* To describe symbolic information for classes and types, the API uses the @@ -272,14 +266,22 @@ * symbolic information, one accepting nominal descriptors, and the other * accepting constant pool entries. * - *

Consistency checks, syntax checks and verification

- * The Class-File API performs checks to ensure arguments are representable in - * the {@code class} file format. A value that is lost when it is built to a - * {@code class} file and re-parsed to a model is rejected with an {@link - * IllegalArgumentException}. For example, a negative value or a value over - * {@code 65535} is lost when built to a {@link ##u2 u2} item, with - * the range {@code [0, 65535]}. In particular, any variable-sized table - * exceeding its maximum representable size is rejected. + *

Consistency checks, syntax checks and verification

+ * The Class-File API performs checks to ensure arguments to construct {@code + * class} file structures are representable in the {@code class} file format. + * An argument value that cannot be representable by its data type is rejected + * with an {@link IllegalArgumentException}. For example, an {@code int} value + * cannot be out of the range of its {@linkplain java.lang.classfile##data-types + * data type}; a {@code List} cannot exceed the maximum representable size of + * its table data type, or contain an unrepresentable element. Restrictions + * based on underlying data type, such as the {@code int} and {@code List} ones + * before, are specified on the corresponding APIs. Unless otherwise noted, in + * all structures, a {@code String} cannot exceed {@code 65535} bytes when + * represented in modified UTF-8 format. + *

+ * Unless otherwise noted, passing null or an array or collection that contains + * null as an element to a constructor or method of any Class-File API class or + * interface will cause a {@link NullPointerException} to be thrown. *

* No consistency checks are performed while building or transforming classfiles * (except for null and representable arguments checks). All builders and diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 962a2057585..ec747d06c21 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -35,6 +35,7 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.PrimitiveClassDescImpl; import jdk.internal.util.ArraysSupport; +import jdk.internal.util.ModifiedUtf; import jdk.internal.vm.annotation.Stable; import static java.util.Objects.requireNonNull; @@ -141,7 +142,7 @@ public abstract sealed class AbstractPoolEntry { @Stable TypeDescriptor typeSym; Utf8EntryImpl(ConstantPool cpm, int index, - byte[] rawBytes, int offset, int rawLen) { + byte[] rawBytes, int offset, int rawLen) { super(cpm, index, 0); this.rawBytes = rawBytes; this.offset = offset; @@ -154,6 +155,10 @@ public abstract sealed class AbstractPoolEntry { } Utf8EntryImpl(ConstantPool cpm, int index, String s, int contentHash) { + // Prevent creation of unwritable entries + if (!ModifiedUtf.isValidLengthInConstantPool(s)) { + throw new IllegalArgumentException("utf8 length out of range of u2: " + ModifiedUtf.utfLen(s)); + } super(cpm, index, 0); this.rawBytes = null; this.offset = 0; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index b30592a4ebd..6daef5cab9a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -277,10 +277,9 @@ public final class BufWriterImpl implements BufWriter { int strlen = str.length(); int countNonZeroAscii = JLA.countNonZeroAscii(str); long utflenLong = utfLen(str, countNonZeroAscii); - if (!ExactConversionsSupport.isLongToCharExact(utflenLong)) { - throw new IllegalArgumentException("utf8 length out of range of u2: " + utflenLong); - } - int utflen = (int)utflenLong; + // Utf8Entry should always be writable + assert ExactConversionsSupport.isLongToCharExact(utflenLong) : utflenLong; + int utflen = (int) utflenLong; reserveSpace(utflen + 3); int offset = this.offset; diff --git a/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java b/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java index 46885e12adf..4221ec22de3 100644 --- a/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java +++ b/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java @@ -28,19 +28,17 @@ package jdk.internal.util; import jdk.internal.vm.annotation.ForceInline; -/** - * Helper to JDK UTF putChar and Calculate length - * - * @since 24 - */ -public abstract class ModifiedUtf { - // Maximum number of bytes allowed for a Modified UTF-8 encoded string - // in a ClassFile constant pool entry (CONSTANT_Utf8_info). +/// Utilities for string encoding and decoding with the +/// [Modified UTF-8][java.io.DataInput##modified-utf-8] format. +public final class ModifiedUtf { + /// Maximum number of bytes allowed for a Modified UTF-8 encoded string + /// in a [java.lang.classfile.constantpool.Utf8Entry] or a hotspot `Symbol`. public static final int CONSTANT_POOL_UTF8_MAX_BYTES = 65535; private ModifiedUtf() { } + /// Writes a char to the pre-sized modified UTF buffer. @ForceInline public static int putChar(byte[] buf, int offset, char c) { if (c != 0 && c < 0x80) { @@ -58,11 +56,23 @@ public abstract class ModifiedUtf { return offset; } - /** - * Calculate the utf length of a string - * @param str input string - * @param countNonZeroAscii the number of non-zero ascii characters in the prefix calculated by JLA.countNonZeroAscii(str) - */ + /// Calculate the encoded length of an input String. + /// For many workloads that have fast paths for ASCII-only prefixes, + /// [#utfLen(String, int)] skips scanning that prefix. + /// + /// @param str input string + public static long utfLen(String str) { + return utfLen(str, 0); + } + + /// Calculate the encoded length of trailing parts of an input String, + /// after [jdk.internal.access.JavaLangAccess#countNonZeroAscii(String)] + /// calculates the number of contiguous single-byte characters in the + /// beginning of the string. + /// + /// @param str input string + /// @param countNonZeroAscii the number of non-zero ascii characters in the + /// prefix calculated by JLA.countNonZeroAscii(str) @ForceInline public static long utfLen(String str, int countNonZeroAscii) { long utflen = str.length(); @@ -74,11 +84,11 @@ public abstract class ModifiedUtf { return utflen; } - /** - * Checks whether the Modified UTF-8 encoded length of the given string - * fits within the ClassFile constant pool limit (u2 length = 65535 bytes). - * @param str the string to check - */ + /// Checks whether an input String can be encoded in a + /// [java.lang.classfile.constantpool.Utf8Entry], or represented as a + /// hotspot `Symbol` (which has the same length limit). + /// + /// @param str input string @ForceInline public static boolean isValidLengthInConstantPool(String str) { // Quick approximation: each char can be at most 3 bytes in Modified UTF-8. @@ -91,7 +101,7 @@ public abstract class ModifiedUtf { return false; } // Check exact Modified UTF-8 length. - long utfLen = utfLen(str, 0); + long utfLen = utfLen(str); return utfLen <= CONSTANT_POOL_UTF8_MAX_BYTES; } } diff --git a/test/jdk/jdk/classfile/LimitsTest.java b/test/jdk/jdk/classfile/LimitsTest.java index 0b347c835db..165f33ff44e 100644 --- a/test/jdk/jdk/classfile/LimitsTest.java +++ b/test/jdk/jdk/classfile/LimitsTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 8320360 8330684 8331320 8331655 8331940 8332486 8335820 8336833 8361635 + * 8367585 * @summary Testing ClassFile limits. * @run junit LimitsTest */ @@ -52,17 +53,23 @@ import java.lang.classfile.instruction.SwitchCase; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; +import java.lang.constant.ModuleDesc; +import java.lang.constant.PackageDesc; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; +import java.util.stream.Stream; import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectCodeBuilder; import jdk.internal.classfile.impl.DirectMethodBuilder; import jdk.internal.classfile.impl.LabelContext; +import jdk.internal.classfile.impl.TemporaryConstantPool; import jdk.internal.classfile.impl.UnboundAttribute; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; @@ -447,4 +454,73 @@ class LimitsTest { var cpb = ConstantPoolBuilder.of(); cpb.intEntry(-cpb.intEntry(0).hashCode()); } + + static List legalStrings() { + var empty = ""; + var allAscii = "e".repeat(0xFFFF); + // 3-byte utf8 characters + var largeChars = String.valueOf((char) 0x800).repeat(0xFFFF / 3); + return List.of(empty, allAscii, largeChars); + } + + @ParameterizedTest + @MethodSource("legalStrings") + void testStringLengthInLimit(String st) { + TemporaryConstantPool.INSTANCE.utf8Entry(st); + ConstantPoolBuilder.of().utf8Entry(st); + } + + static List oversizedStrings() { + var allAscii = "e".repeat(0x10000); + // 3-byte utf8 characters + var largeChars = String.valueOf((char) 0x800).repeat(0xFFFF / 3 + 1); + return List.of(allAscii, largeChars); + } + + @ParameterizedTest + @MethodSource("oversizedStrings") + void testStringLengthOverLimit(String st) { + assertThrows(IllegalArgumentException.class, () -> TemporaryConstantPool.INSTANCE.utf8Entry(st)); + assertThrows(IllegalArgumentException.class, () -> ConstantPoolBuilder.of().utf8Entry(st)); + } + + static Stream pools() { + return Stream.of(ConstantPoolBuilder.of(), TemporaryConstantPool.INSTANCE); + } + + @ParameterizedTest + @MethodSource("pools") + void testSingleReferenceNominalDescriptorOverLimit(ConstantPoolBuilder cpb) { + var fittingName = "A" + "a".repeat(65532); // fits "enveloped" L ; + var borderName = "B" + "b".repeat(65534); // fits only "not enveloped" + var overflowName = "C" + "b".repeat(65535); // nothing fits + + var fittingClassDesc = ClassDesc.of(fittingName); + var borderClassDesc = ClassDesc.of(borderName); + var overflowClassDesc = ClassDesc.of(overflowName); + cpb.classEntry(fittingClassDesc); + cpb.utf8Entry(fittingClassDesc); + cpb.classEntry(borderClassDesc); + assertThrows(IllegalArgumentException.class, () -> cpb.utf8Entry(borderClassDesc)); + assertThrows(IllegalArgumentException.class, () -> cpb.classEntry(overflowClassDesc)); + assertThrows(IllegalArgumentException.class, () -> cpb.utf8Entry(overflowClassDesc)); + + cpb.packageEntry(PackageDesc.of(borderName)); + assertThrows(IllegalArgumentException.class, () -> cpb.packageEntry(PackageDesc.of(overflowName))); + cpb.moduleEntry(ModuleDesc.of(borderName)); + assertThrows(IllegalArgumentException.class, () -> cpb.moduleEntry(ModuleDesc.of(overflowName))); + } + + @ParameterizedTest + @MethodSource("pools") + void testMethodTypeDescOverLimit(ConstantPoolBuilder cpb) { + var borderReturnMtd = MethodTypeDesc.of(ClassDesc.of("R" + "r".repeat(65530))); + var overflowReturnMtd = MethodTypeDesc.of(ClassDesc.of("R" + "r".repeat(65531))); + var borderParamMtd = MethodTypeDesc.of(CD_void, ClassDesc.of("P" + "p".repeat(65529))); + var overflowParamMtd = MethodTypeDesc.of(CD_void, ClassDesc.of("P" + "p".repeat(65530))); + cpb.utf8Entry(borderParamMtd); + cpb.utf8Entry(borderReturnMtd); + assertThrows(IllegalArgumentException.class, () -> cpb.utf8Entry(overflowReturnMtd)); + assertThrows(IllegalArgumentException.class, () -> cpb.utf8Entry(overflowParamMtd)); + } } diff --git a/test/jdk/jdk/classfile/SignaturesTest.java b/test/jdk/jdk/classfile/SignaturesTest.java index 95fa8be4602..bf437aa5c35 100644 --- a/test/jdk/jdk/classfile/SignaturesTest.java +++ b/test/jdk/jdk/classfile/SignaturesTest.java @@ -24,10 +24,11 @@ /* * @test * @summary Testing Signatures. - * @bug 8321540 8319463 8357955 8368050 8368331 + * @bug 8321540 8319463 8357955 8368050 8367585 8368331 * @run junit SignaturesTest */ import java.io.IOException; +import java.lang.classfile.attribute.SignatureAttribute; import java.lang.constant.ClassDesc; import java.net.URI; import java.nio.file.FileSystem; @@ -35,6 +36,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @@ -52,9 +54,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static helpers.ClassRecord.assertEqualsDeep; +import static org.junit.jupiter.api.Assertions.assertThrows; import static java.lang.constant.ConstantDescs.*; -import java.util.function.Consumer; -import java.util.function.Function; class SignaturesTest { @@ -270,114 +271,134 @@ class SignaturesTest { "ClassDesc derived from signature"); } - @Test - void testBadTypeSignatures() { - """ - LObject - LObject;B - LIterable - LIterable<< - TBar - TBar - B - B;V - X - [LObject - [LIterable - [LIterable<< - [TBar - [TBar - [B - [X - LSet<+Kind<**>;>; - LSet;>; - ()V - Ljava/util/Optional; - Lcom/example/Outer.package/Inner<[I>; - LSample>; - LSample:Other; - LOuter<[JTT;>.[Inner; - TA:J; - LEmpty<>; - L - Lcom - Lcom/example/ - Lcom/example/Outer< - Lcom/example/Outer - Lcom/example/Outer. - Lcom/example/Outer.Inner<[I> - [V - """.lines().forEach(assertThrows(Signature::parseFrom)); + static Stream badTypeSignatures() { + return """ + LObject + LObject;B + LIterable + LIterable<< + TBar + TBar + B + B;V + X + [LObject + [LIterable + [LIterable<< + [TBar + [TBar + [B + [X + LSet<+Kind<**>;>; + LSet;>; + ()V + Ljava/util/Optional; + Lcom/example/Outer.package/Inner<[I>; + LSample>; + LSample:Other; + LOuter<[JTT;>.[Inner; + TA:J; + LEmpty<>; + L + Lcom + Lcom/example/ + Lcom/example/Outer< + Lcom/example/Outer + Lcom/example/Outer. + Lcom/example/Outer.Inner<[I> + [V + """.lines(); } - @Test - void testGoodTypeSignatures() { - """ - Ljava/util/Optional; - Lcom/example/Outer.Inner<[I>; - LSample; - LOuter<[JTT;>.Inner; - LOuter.Inner; - """.lines().forEach(Signature::parseFrom); + @ParameterizedTest + @MethodSource("badTypeSignatures") + void testBadTypeSignatures(String s) { + assertThrows(IllegalArgumentException.class, () -> Signature.parseFrom(s)); } - @Test - void testBadClassSignatures() { - """ - Ljava/lang/Object;Ljava/lang/Iterable - LObject - LObject;B - LIterable - LIterable<< - TBar - TBar - B - B;V - X - LFoo.It;L - LFoo;LFoo;LBar; - >LFoo; - LFoo<+>; - ()V - Ljava/lang/Object;TK; - Ljava/lang/Object;[Ljava/lang/Object; - [Ljava/util/Optional<[I>; - [I - TK; - Ljava/lang/Object; - <:Ljava/lang/Object;>Ljava/lang/Object; - <>Ljava/lang/Object; - """.lines().forEach(assertThrows(ClassSignature::parseFrom)); + static Stream goodTypeSignatures() { + return """ + Ljava/util/Optional; + Lcom/example/Outer.Inner<[I>; + LSample; + LOuter<[JTT;>.Inner; + LOuter.Inner; + """.lines(); } - @Test - void testBadMethodSignatures() { - """ - LObject; - B - ()V^ - ()V^B - ()V^X - (LObject;) - (LObject)V - ()LIterable - ()LIterable<< - ()TBar - ()TBar;B - (TBar)V - (B)V - (X) - ()X - ()VB - ()LSet<+Kind<**>;>; - (LSet;>;)V - ()V - (TT;I)VI - (V)V - """.lines().forEach(assertThrows(MethodSignature::parseFrom)); + @ParameterizedTest + @MethodSource("goodTypeSignatures") + void testGoodTypeSignature(String s) { + Signature.parseFrom(s); + } + + static Stream badClassSignatures() { + return """ + Ljava/lang/Object;Ljava/lang/Iterable + LObject + LObject;B + LIterable + LIterable<< + TBar + TBar + B + B;V + X + LFoo.It;L + LFoo;LFoo;LBar; + >LFoo; + LFoo<+>; + ()V + Ljava/lang/Object;TK; + Ljava/lang/Object;[Ljava/lang/Object; + [Ljava/util/Optional<[I>; + [I + TK; + Ljava/lang/Object; + <:Ljava/lang/Object;>Ljava/lang/Object; + <>Ljava/lang/Object; + """.lines(); + } + + @ParameterizedTest + @MethodSource("badClassSignatures") + void testBadClassSignature(String s) { + assertThrows(IllegalArgumentException.class, () -> ClassSignature.parseFrom(s)); + } + + static Stream badMethodSignatures() { + return """ + LObject; + B + ()V^ + ()V^B + ()V^X + (LObject;) + (LObject)V + ()LIterable + ()LIterable + ()TBar + ()TBar;B + (TBar)V + (X) + ()X + ()VB + ()LSet<+Kin + (LSet()V + (TT;I)VI + (V)V + """.lines(); + } + + @ParameterizedTest + @MethodSource("badMethodSignatures") + void testBadMethodSignature(String s) { + assertThrows(IllegalArgumentException.class, () -> MethodSignature.parseFrom(s)); } @Test @@ -385,16 +406,76 @@ class SignaturesTest { var sig = Signature.parseFrom("I"); var arrSig = Signature.parseFrom("[I"); for (int dim : List.of(256, -1, 0)) - Assertions.assertThrows(IllegalArgumentException.class, () -> Signature.ArrayTypeSig.of(dim, sig)); + assertThrows(IllegalArgumentException.class, () -> Signature.ArrayTypeSig.of(dim, sig)); for (int dim : List.of(255, -1, 0)) - Assertions.assertThrows(IllegalArgumentException.class, () -> Signature.ArrayTypeSig.of(dim, arrSig)); + assertThrows(IllegalArgumentException.class, () -> Signature.ArrayTypeSig.of(dim, arrSig)); for (int dim : List.of(255, 1)) Signature.ArrayTypeSig.of(dim, sig); for (int dim : List.of(254, 1)) Signature.ArrayTypeSig.of(dim, arrSig); } - private Consumer assertThrows(Function parser) { - return s -> Assertions.assertThrows(IllegalArgumentException.class, () -> parser.apply(s), s); + static Stream longTypeSignatures() { + var longAsciiName = "A" + "a".repeat(65536); + var longCharName = "§".repeat(32768); + var simpleClassSig = ClassTypeSig.of(longAsciiName); + var nestedSig = ClassTypeSig.of(simpleClassSig, longCharName); + var typeVarSig = TypeVarSig.of(longCharName); + var parameterizedSig = ClassTypeSig.of(longCharName, TypeArg.of(nestedSig), TypeArg.unbounded()); + var parameterizedNestedSig = ClassTypeSig.of(nestedSig, longAsciiName, TypeArg.superOf(simpleClassSig)); + return Stream.of(simpleClassSig, nestedSig, typeVarSig, parameterizedSig, parameterizedNestedSig); + } + + @ParameterizedTest + @MethodSource("longTypeSignatures") + void testLongTypeSignature(Signature sig) { + var st = sig.signatureString(); + Signature.parseFrom(st); // Valid signature + assertThrows(IllegalArgumentException.class, () -> SignatureAttribute.of(sig)); // Cannot write to class + } + + static Stream longClassSignatures() { + var longAsciiName = "A" + "a".repeat(65536); + var longCharName = "§".repeat(32768); + var simpleClassSig = ClassTypeSig.of(longAsciiName); + var longSuperClass = ClassSignature.of(simpleClassSig); + var longNameParam = TypeParam.of(longCharName, ClassTypeSig.of(CD_String)); + var longBoundParam = TypeParam.of("T", simpleClassSig); + var longNameParamClass = ClassSignature.of(List.of(longNameParam), ClassTypeSig.of(CD_Object)); + var longBoundParamClass = ClassSignature.of(List.of(longBoundParam), ClassTypeSig.of(CD_Number)); + return Stream.of(longSuperClass, longNameParamClass, longBoundParamClass); + } + + @ParameterizedTest + @MethodSource("longClassSignatures") + void testLongClassSignature(ClassSignature sig) { + var st = sig.signatureString(); + ClassSignature.parseFrom(st); // Valid signature + assertThrows(IllegalArgumentException.class, () -> SignatureAttribute.of(sig)); // Cannot write to class + } + + static Stream longMethodSignatures() { + var longAsciiName = "A" + "a".repeat(65536); + var longCharName = "§".repeat(32768); + var simpleClassSig = ClassTypeSig.of(longAsciiName); + var longNameTypeVar = TypeVarSig.of(longCharName); + var longReturnMethod = MethodSignature.of(simpleClassSig); + var longNameParam = TypeParam.of(longCharName, ClassTypeSig.of(CD_String)); + var longNameParamMethod = MethodSignature.of(List.of(longNameParam), List.of(), BaseTypeSig.of(CD_void)); + var longThrowMethod = MethodSignature.of(List.of(), List.of(longNameTypeVar), ClassTypeSig.of(CD_Number)); + var longParameterMethod = MethodSignature.of(BaseTypeSig.of(CD_int), simpleClassSig); + + var eachParameter = ClassTypeSig.of("A" + "a".repeat(250)); + var parameterArray = Collections.nCopies(300, eachParameter).toArray(Signature[]::new); + var manyParameterMethod = MethodSignature.of(BaseTypeSig.of(CD_void), parameterArray); + return Stream.of(longReturnMethod, longNameParamMethod, longThrowMethod, longParameterMethod, manyParameterMethod); + } + + @ParameterizedTest + @MethodSource("longMethodSignatures") + void testLongMethodSignature(MethodSignature sig) { + var st = sig.signatureString(); + MethodSignature.parseFrom(st); // Valid signature + assertThrows(IllegalArgumentException.class, () -> SignatureAttribute.of(sig)); // Cannot write to class } } diff --git a/test/jdk/jdk/internal/util/ModifiedUtfTest.java b/test/jdk/jdk/internal/util/ModifiedUtfTest.java index 7572acfba24..3376a8eb6ae 100644 --- a/test/jdk/jdk/internal/util/ModifiedUtfTest.java +++ b/test/jdk/jdk/internal/util/ModifiedUtfTest.java @@ -104,19 +104,6 @@ public class ModifiedUtfTest { } catch (UTFDataFormatException e) { } - BufWriterImpl bufWriter = new BufWriterImpl(ConstantPoolBuilder.of(), (ClassFileImpl) ClassFile.of()); - Method writeUtfEntry = bufWriter.getClass().getDeclaredMethod("writeUtfEntry", String.class); - writeUtfEntry.setAccessible(true); - try { - writeUtfEntry.invoke(bufWriter, largeString); - throw new RuntimeException("Expected IllegalArgumentException was not thrown."); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (!(cause instanceof IllegalArgumentException)) { - throw new RuntimeException("Expected IllegalArgumentException was not thrown."); - } - } - /** * In the writeUTF function, utfLen is used to calculate the length of the string to be written * and store it in the stream header. This test uses the HeaderCaptureOutputStream inner class