8367585: Prevent creation of unrepresentable Utf8Entry

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2025-11-21 15:39:05 +00:00
parent e439909b7d
commit 3b1eb76231
10 changed files with 327 additions and 170 deletions

View File

@ -32,9 +32,8 @@
* including {@link Attribute}, {@link AttributedElement}, {@link AttributeMapper}, and {@link CustomAttribute}, which
* do not reside in this package.
* <p>
* 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.
*
* <h2 id="reading">Reading Attributes</h2>
* The general way to obtain attributes is through {@link AttributedElement}. In addition to that, many attributes

View File

@ -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.
* <p>
* 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.
*
* <h2 id="reading">Reading the constant pool entries</h2>
* When read from {@code class} files, the pool entries are lazily inflated; the contents of these entries, besides the

View File

@ -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.
* <p>
* 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.
*
* <h2 id="reading">Reading of instructions</h2>
* Instructions and pseudo-instructions are usually accessed from a {@link CodeModel}, such as {@link CodeModel#forEach

View File

@ -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).
* <p>
* 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.
*
* <h3>Symbolic information</h3>
* 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.
*
* <h3>Consistency checks, syntax checks and verification</h3>
* 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.
* <h3 id="checks">Consistency checks, syntax checks and verification</h3>
* 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.
* <p>
* 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.
* <p>
* No consistency checks are performed while building or transforming classfiles
* (except for null and representable arguments checks). All builders and

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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<String> 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<String> 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<ConstantPoolBuilder> 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));
}
}

View File

@ -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<LFoo>
LIterable<<
TBar
TBar<LFoo;>
B<LFoo;>
B<LFoo;>;V
X
[LObject
[LIterable<LFoo>
[LIterable<<
[TBar
[TBar<LFoo;>
[B<LFoo;>
[X
LSet<+Kind<**>;>;
LSet<?Kind<*>;>;
()V
Ljava/util/Opt<Ljava/lang/Integer;>ional;
Lcom/example/Outer<Ljava/lang/String;>.package/Inner<[I>;
LSample>;
LSample:Other;
LOuter<[JTT;>.[Inner;
TA:J;
LEmpty<>;
L
Lcom
Lcom/example/
Lcom/example/Outer<
Lcom/example/Outer<Ljava/
Lcom/example/Outer<Ljava/lang/String
Lcom/example/Outer<Ljava/lang/String;
Lcom/example/Outer<Ljava/lang/String;>
Lcom/example/Outer<Ljava/lang/String;>.
Lcom/example/Outer<Ljava/lang/String;>.Inner<[I>
[V
""".lines().forEach(assertThrows(Signature::parseFrom));
static Stream<String> badTypeSignatures() {
return """
LObject
LObject;B
LIterable<LFoo>
LIterable<<
TBar
TBar<LFoo;>
B<LFoo;>
B<LFoo;>;V
X
[LObject
[LIterable<LFoo>
[LIterable<<
[TBar
[TBar<LFoo;>
[B<LFoo;>
[X
LSet<+Kind<**>;>;
LSet<?Kind<*>;>;
()V
Ljava/util/Opt<Ljava/lang/Integer;>ional;
Lcom/example/Outer<Ljava/lang/String;>.package/Inner<[I>;
LSample>;
LSample:Other;
LOuter<[JTT;>.[Inner;
TA:J;
LEmpty<>;
L
Lcom
Lcom/example/
Lcom/example/Outer<
Lcom/example/Outer<Ljava/
Lcom/example/Outer<Ljava/lang/String
Lcom/example/Outer<Ljava/lang/String;
Lcom/example/Outer<Ljava/lang/String;>
Lcom/example/Outer<Ljava/lang/String;>.
Lcom/example/Outer<Ljava/lang/String;>.Inner<[I>
[V
""".lines();
}
@Test
void testGoodTypeSignatures() {
"""
Ljava/util/Optional<Ljava/lang/Integer;>;
Lcom/example/Outer<Ljava/lang/Integer;>.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<LFoo;>
LObject
LObject;B
LIterable<LFoo>
LIterable<<
TBar
TBar<LFoo;>
B<LFoo;>
B<LFoo;>;V
X
LFoo<TK;>.It;L
<K+LObject;>LFoo<TK;;>;LFoo<TK;>;LBar;
<K:LObject;>>LFoo<TK;>;
<K:LObject;>LFoo<+>;
()V
<K:Ljava/lang/Object;>Ljava/lang/Object;TK;
Ljava/lang/Object;[Ljava/lang/Object;
[Ljava/util/Optional<[I>;
[I
<K:Ljava/lang/Object;>TK;
<K;Q:Ljava/lang/Object;>Ljava/lang/Object;
<:Ljava/lang/Object;>Ljava/lang/Object;
<>Ljava/lang/Object;
""".lines().forEach(assertThrows(ClassSignature::parseFrom));
static Stream<String> goodTypeSignatures() {
return """
Ljava/util/Optional<Ljava/lang/Integer;>;
Lcom/example/Outer<Ljava/lang/Integer;>.Inner<[I>;
LSample;
LOuter<[JTT;>.Inner;
LOuter.Inner;
""".lines();
}
@Test
void testBadMethodSignatures() {
"""
LObject;
B
()V^
()V^B
()V^X
(LObject;)
(LObject)V
()LIterable<LFoo>
()LIterable<<
()TBar
()TBar;B
(TBar<LFoo;>)V
(B<LFoo;>)V
(X)
()X
()VB
()LSet<+Kind<**>;>;
(LSet<?Kind<*>;>;)V
<T::LA>()V
(TT;I)VI
(V)V
""".lines().forEach(assertThrows(MethodSignature::parseFrom));
@ParameterizedTest
@MethodSource("goodTypeSignatures")
void testGoodTypeSignature(String s) {
Signature.parseFrom(s);
}
static Stream<String> badClassSignatures() {
return """
Ljava/lang/Object;Ljava/lang/Iterable<LFoo;>
LObject
LObject;B
LIterable<LFoo>
LIterable<<
TBar
TBar<LFoo;>
B<LFoo;>
B<LFoo;>;V
X
LFoo<TK;>.It;L
<K+LObject;>LFoo<TK;;>;LFoo<TK;>;LBar;
<K:LObject;>>LFoo<TK;>;
<K:LObject;>LFoo<+>;
()V
<K:Ljava/lang/Object;>Ljava/lang/Object;TK;
Ljava/lang/Object;[Ljava/lang/Object;
[Ljava/util/Optional<[I>;
[I
<K:Ljava/lang/Object;>TK;
<K;Q:Ljava/lang/Object;>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<String> badMethodSignatures() {
return """
LObject;
B
()V^
()V^B
()V^X
(LObject;)
(LObject)V
()LIterable
()LIterable
()TBar
()TBar;B
(TBar<LFoo;
(B<LFoo;>)V
(X)
()X
()VB
()LSet<+Kin
(LSet<?Kind
<T::LA>()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<String> assertThrows(Function<String, ?> parser) {
return s -> Assertions.assertThrows(IllegalArgumentException.class, () -> parser.apply(s), s);
static Stream<Signature> 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<ClassSignature> 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<MethodSignature> 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
}
}

View File

@ -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