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