From 431f46724658b703e995e518cb7a2149c50d6a9d Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 3 Sep 2025 19:21:38 +0000 Subject: [PATCH] 8361635: Missing List length validation in the Class-File API Reviewed-by: asotona --- .../java/lang/classfile/Annotation.java | 8 + .../lang/classfile/AnnotationElement.java | 2 + .../java/lang/classfile/AnnotationValue.java | 7 +- .../java/lang/classfile/ClassBuilder.java | 12 +- .../java/lang/classfile/CodeBuilder.java | 4 +- .../java/lang/classfile/Interfaces.java | 12 +- .../java/lang/classfile/TypeAnnotation.java | 9 +- .../CharacterRangeTableAttribute.java | 2 + .../attribute/ExceptionsAttribute.java | 8 + .../attribute/InnerClassesAttribute.java | 4 + .../attribute/LineNumberTableAttribute.java | 2 + .../LocalVariableTableAttribute.java | 2 + .../LocalVariableTypeTableAttribute.java | 2 + .../attribute/MethodParametersAttribute.java | 4 + .../classfile/attribute/ModuleAttribute.java | 23 +- .../classfile/attribute/ModuleExportInfo.java | 28 +- .../attribute/ModuleHashesAttribute.java | 8 + .../classfile/attribute/ModuleOpenInfo.java | 24 +- .../attribute/ModulePackagesAttribute.java | 8 + .../attribute/ModuleProvideInfo.java | 10 +- .../attribute/NestMembersAttribute.java | 12 +- .../PermittedSubclassesAttribute.java | 12 +- .../classfile/attribute/RecordAttribute.java | 4 + .../attribute/RecordComponentInfo.java | 8 + .../RuntimeInvisibleAnnotationsAttribute.java | 4 + ...nvisibleParameterAnnotationsAttribute.java | 4 + ...timeInvisibleTypeAnnotationsAttribute.java | 4 + .../RuntimeVisibleAnnotationsAttribute.java | 4 + ...eVisibleParameterAnnotationsAttribute.java | 4 + ...untimeVisibleTypeAnnotationsAttribute.java | 4 + .../attribute/StackMapFrameInfo.java | 2 + .../attribute/StackMapTableAttribute.java | 2 + .../constantpool/ConstantPoolBuilder.java | 4 + .../classfile/constantpool/Utf8Entry.java | 3 +- .../java/lang/classfile/package-info.java | 2 +- .../classfile/impl/AnnotationImpl.java | 6 +- .../classfile/impl/AttributeHolder.java | 3 +- .../impl/BootstrapMethodEntryImpl.java | 8 +- .../classfile/impl/BufWriterImpl.java | 4 +- .../classfile/impl/DirectCodeBuilder.java | 11 +- .../classfile/impl/InterfacesImpl.java | 4 +- .../classfile/impl/SplitConstantPool.java | 10 +- .../classfile/impl/StackMapDecoder.java | 6 +- .../classfile/impl/TargetInfoImpl.java | 2 +- .../classfile/impl/UnboundAttribute.java | 74 ++-- .../jdk/internal/classfile/impl/Util.java | 29 ++ test/jdk/jdk/classfile/LimitsTest.java | 220 ++++++++++- .../jdk/jdk/classfile/ListValidationTest.java | 348 ++++++++++++++++++ 48 files changed, 866 insertions(+), 111 deletions(-) create mode 100644 test/jdk/jdk/classfile/ListValidationTest.java diff --git a/src/java.base/share/classes/java/lang/classfile/Annotation.java b/src/java.base/share/classes/java/lang/classfile/Annotation.java index 5c4ee630b08..5a9aeaccfc4 100644 --- a/src/java.base/share/classes/java/lang/classfile/Annotation.java +++ b/src/java.base/share/classes/java/lang/classfile/Annotation.java @@ -95,6 +95,8 @@ public sealed interface Annotation * @param annotationClass the constant pool entry holding the descriptor string * of the annotation interface * @param elements the element-value pairs of the annotation + * @throws IllegalArgumentException if the number of pairs exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static Annotation of(Utf8Entry annotationClass, List elements) { @@ -106,6 +108,8 @@ public sealed interface Annotation * @param annotationClass the constant pool entry holding the descriptor string * of the annotation interface * @param elements the element-value pairs of the annotation + * @throws IllegalArgumentException if the number of pairs exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static Annotation of(Utf8Entry annotationClass, AnnotationElement... elements) { @@ -116,6 +120,8 @@ public sealed interface Annotation * {@return an annotation} * @param annotationClass the descriptor of the annotation interface * @param elements the element-value pairs of the annotation + * @throws IllegalArgumentException if the number of pairs exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static Annotation of(ClassDesc annotationClass, List elements) { @@ -126,6 +132,8 @@ public sealed interface Annotation * {@return an annotation} * @param annotationClass the descriptor of the annotation interface * @param elements the element-value pairs of the annotation + * @throws IllegalArgumentException if the number of pairs exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static Annotation of(ClassDesc annotationClass, AnnotationElement... elements) { diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java b/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java index 23f830f1512..aed9a358535 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java @@ -209,6 +209,8 @@ public sealed interface AnnotationElement * @param name the name of the key * @param values the associated values * @see AnnotationValue#ofArray(AnnotationValue...) AnnotationValue::ofArray + * @throws IllegalArgumentException if the number of associated values + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static AnnotationElement ofArray(String name, AnnotationValue... values) { diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java index ae31fdf3967..623cb5a771f 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -673,6 +673,8 @@ public sealed interface AnnotationValue { * on array values derived from Java source code. * * @param values the array elements + * @throws IllegalArgumentException if the length of array exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static OfArray ofArray(List values) { return new AnnotationImpl.OfArrayImpl(values); @@ -686,6 +688,8 @@ public sealed interface AnnotationValue { * on array values derived from Java source code. * * @param values the array elements + * @throws IllegalArgumentException if the length of array exceeds the limit + * of {@link java.lang.classfile##u2 u2} */ static OfArray ofArray(AnnotationValue... values) { return ofArray(List.of(values)); @@ -699,7 +703,8 @@ public sealed interface AnnotationValue { * @param value the annotation value * @throws IllegalArgumentException when the {@code value} parameter is not * a primitive, a wrapper of primitive, a String, a ClassDesc, - * an enum constant, or an array of one of these. + * an enum constant, or an array of one of these; or any array has + * length over the limit of {@link java.lang.classfile##u2 u2} */ static AnnotationValue of(Object value) { if (value instanceof String s) { diff --git a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java index 86cdb298d9e..7fd10b9dab1 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java @@ -131,6 +131,8 @@ public sealed interface ClassBuilder * * @param interfaces the interfaces * @return this builder + * @throws IllegalArgumentException if the number of interfaces exceeds the + * limit of {@link java.lang.classfile##u2 u2} * @see Interfaces */ default ClassBuilder withInterfaces(List interfaces) { @@ -142,6 +144,8 @@ public sealed interface ClassBuilder * * @param interfaces the interfaces * @return this builder + * @throws IllegalArgumentException if the number of interfaces exceeds the + * limit of {@link java.lang.classfile##u2 u2} * @see Interfaces */ default ClassBuilder withInterfaces(ClassEntry... interfaces) { @@ -153,7 +157,9 @@ public sealed interface ClassBuilder * * @param interfaces the interfaces * @return this builder - * @throws IllegalArgumentException if any element of {@code interfaces} is primitive + * @throws IllegalArgumentException if any of {@code interfaces} is primitive, + * or if the number of interfaces exceeds the limit of {@link + * java.lang.classfile##u2 u2} * @see Interfaces */ default ClassBuilder withInterfaceSymbols(List interfaces) { @@ -165,7 +171,9 @@ public sealed interface ClassBuilder * * @param interfaces the interfaces * @return this builder - * @throws IllegalArgumentException if any element of {@code interfaces} is primitive + * @throws IllegalArgumentException if any of {@code interfaces} is primitive, + * or if the number of interfaces exceeds the limit of {@link + * java.lang.classfile##u2 u2} * @see Interfaces */ default ClassBuilder withInterfaceSymbols(ClassDesc... interfaces) { diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java index 656e84adf58..b4d3e427e91 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -2402,8 +2402,8 @@ public sealed interface CodeBuilder * variable by a constant. *

* This may also generate {@link Opcode#IINC_W wide iinc} instructions if - * {@code slot} exceeds {@code 255} or {@code val} exceeds the range of - * {@link TypeKind#BYTE byte}. + * {@code slot} exceeds the limit of {@link java.lang.classfile##u1 u1} or + * {@code val} exceeds the range of {@link TypeKind#BYTE byte}. * * @param slot the local variable slot * @param val the increment value diff --git a/src/java.base/share/classes/java/lang/classfile/Interfaces.java b/src/java.base/share/classes/java/lang/classfile/Interfaces.java index bd89c494f24..d4fac8e310a 100644 --- a/src/java.base/share/classes/java/lang/classfile/Interfaces.java +++ b/src/java.base/share/classes/java/lang/classfile/Interfaces.java @@ -54,6 +54,8 @@ public sealed interface Interfaces /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces + * @throws IllegalArgumentException if the number of interfaces + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static Interfaces of(List interfaces) { return new InterfacesImpl(interfaces); @@ -62,6 +64,8 @@ public sealed interface Interfaces /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces + * @throws IllegalArgumentException if the number of interfaces + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static Interfaces of(ClassEntry... interfaces) { return of(List.of(interfaces)); @@ -70,7 +74,9 @@ public sealed interface Interfaces /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces - * @throws IllegalArgumentException if any of {@code interfaces} is primitive + * @throws IllegalArgumentException if any of {@code interfaces} is primitive, + * or if the number of interfaces exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static Interfaces ofSymbols(List interfaces) { return of(Util.entryList(interfaces)); @@ -79,7 +85,9 @@ public sealed interface Interfaces /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces - * @throws IllegalArgumentException if any of {@code interfaces} is primitive + * @throws IllegalArgumentException if any of {@code interfaces} is primitive, + * or if the number of interfaces exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static Interfaces ofSymbols(ClassDesc... interfaces) { return ofSymbols(Arrays.asList(interfaces)); diff --git a/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java b/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java index 09dc3b59098..23c735257ae 100644 --- a/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java +++ b/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java @@ -182,6 +182,8 @@ public sealed interface TypeAnnotation * @param targetInfo which type in a declaration or expression is annotated * @param targetPath which part of the type is annotated * @param annotation the annotation + * @throws IllegalArgumentException if the size of {@code targetPath} + * exceeds the limit of {@link java.lang.classfile##u1 u1} */ static TypeAnnotation of(TargetInfo targetInfo, List targetPath, Annotation annotation) { @@ -486,6 +488,8 @@ public sealed interface TypeAnnotation * including a variable declared as a resource in a try-with-resources statement} * @param targetType {@link TargetType#LOCAL_VARIABLE} or {@link TargetType#RESOURCE_VARIABLE} * @param table the list of local variable targets + * @throws IllegalArgumentException if the size of the list of targets + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static LocalVarTarget ofVariable(TargetType targetType, List table) { return new TargetInfoImpl.LocalVarTargetImpl(targetType, table); @@ -494,6 +498,8 @@ public sealed interface TypeAnnotation /** * {@return a target for annotations on the type in a local variable declaration} * @param table the list of local variable targets + * @throws IllegalArgumentException if the size of the list of targets + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static LocalVarTarget ofLocalVariable(List table) { return ofVariable(TargetType.LOCAL_VARIABLE, table); @@ -503,6 +509,8 @@ public sealed interface TypeAnnotation * {@return a target for annotations on the type in a local variable declared * as a resource in a try-with-resources statement} * @param table the list of local variable targets + * @throws IllegalArgumentException if the size of the list of targets + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static LocalVarTarget ofResourceVariable(List table) { return ofVariable(TargetType.RESOURCE_VARIABLE, table); @@ -802,7 +810,6 @@ public sealed interface TypeAnnotation */ Label startLabel(); - /** * The given local variable has a value at indices into the code array in the interval * [start_pc, start_pc + length), that is, between start_pc inclusive and start_pc + length exclusive. diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeTableAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeTableAttribute.java index 49168ed99f8..45af5c20c0c 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeTableAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeTableAttribute.java @@ -88,6 +88,8 @@ public sealed interface CharacterRangeTableAttribute * {@link CodeBuilder#characterRange CodeBuilder::characterRange} instead. * * @param ranges the descriptions of the character ranges + * @throws IllegalArgumentException if the number of ranges exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static CharacterRangeTableAttribute of(List ranges) { return new UnboundAttribute.UnboundCharacterRangeTableAttribute(ranges); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ExceptionsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/ExceptionsAttribute.java index acfd8bcca94..260cbe223e2 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ExceptionsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ExceptionsAttribute.java @@ -77,6 +77,8 @@ public sealed interface ExceptionsAttribute /** * {@return an {@code Exceptions} attribute} * @param exceptions the exceptions that may be thrown from this method + * @throws IllegalArgumentException if the number of exceptions exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ExceptionsAttribute of(List exceptions) { return new UnboundAttribute.UnboundExceptionsAttribute(exceptions); @@ -85,6 +87,8 @@ public sealed interface ExceptionsAttribute /** * {@return an {@code Exceptions} attribute} * @param exceptions the exceptions that may be thrown from this method + * @throws IllegalArgumentException if the number of exceptions exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ExceptionsAttribute of(ClassEntry... exceptions) { return of(List.of(exceptions)); @@ -93,6 +97,8 @@ public sealed interface ExceptionsAttribute /** * {@return an {@code Exceptions} attribute} * @param exceptions the exceptions that may be thrown from this method + * @throws IllegalArgumentException if the number of exceptions exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ExceptionsAttribute ofSymbols(List exceptions) { return of(Util.entryList(exceptions)); @@ -101,6 +107,8 @@ public sealed interface ExceptionsAttribute /** * {@return an {@code Exceptions} attribute} * @param exceptions the exceptions that may be thrown from this method + * @throws IllegalArgumentException if the number of exceptions exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ExceptionsAttribute ofSymbols(ClassDesc... exceptions) { return ofSymbols(Arrays.asList(exceptions)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassesAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassesAttribute.java index b50b38d0a00..a9e8e67805d 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassesAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassesAttribute.java @@ -65,6 +65,8 @@ public sealed interface InnerClassesAttribute /** * {@return an {@code InnerClasses} attribute} * @param innerClasses descriptions of the nested classes + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static InnerClassesAttribute of(List innerClasses) { return new UnboundAttribute.UnboundInnerClassesAttribute(innerClasses); @@ -73,6 +75,8 @@ public sealed interface InnerClassesAttribute /** * {@return an {@code InnerClasses} attribute} * @param innerClasses descriptions of the nested classes + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static InnerClassesAttribute of(InnerClassInfo... innerClasses) { return new UnboundAttribute.UnboundInnerClassesAttribute(List.of(innerClasses)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/LineNumberTableAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/LineNumberTableAttribute.java index bb08beacaa2..0bcac754677 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/LineNumberTableAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/LineNumberTableAttribute.java @@ -79,6 +79,8 @@ public sealed interface LineNumberTableAttribute * order instead. * * @param lines the line number descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static LineNumberTableAttribute of(List lines) { return new UnboundAttribute.UnboundLineNumberTableAttribute(lines); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTableAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTableAttribute.java index 6c1795e17bf..ad01a5c13ac 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTableAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTableAttribute.java @@ -83,6 +83,8 @@ public sealed interface LocalVariableTableAttribute * {@link CodeBuilder#localVariable CodeBuilder::localVariable} instead. * * @param locals the local variable descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static LocalVariableTableAttribute of(List locals) { return new UnboundAttribute.UnboundLocalVariableTableAttribute(locals); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTypeTableAttribute.java index b4bc80cdca1..180d30c321e 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTypeTableAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -79,6 +79,8 @@ public sealed interface LocalVariableTypeTableAttribute /** * {@return a {@code LocalVariableTypeTable} attribute} * @param locals the local variable descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static LocalVariableTypeTableAttribute of(List locals) { return new UnboundAttribute.UnboundLocalVariableTypeTableAttribute(locals); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/MethodParametersAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/MethodParametersAttribute.java index 8cf6d7b6319..c86a0700204 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/MethodParametersAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/MethodParametersAttribute.java @@ -68,6 +68,8 @@ public sealed interface MethodParametersAttribute /** * {@return a {@code MethodParameters} attribute} * @param parameters the method parameter descriptions + * @throws IllegalArgumentException if the number of parameters exceeds the + * limit of {@link java.lang.classfile##u1 u1} */ static MethodParametersAttribute of(List parameters) { return new UnboundAttribute.UnboundMethodParametersAttribute(parameters); @@ -76,6 +78,8 @@ public sealed interface MethodParametersAttribute /** * {@return a {@code MethodParameters} attribute} * @param parameters the method parameter descriptions + * @throws IllegalArgumentException if the number of parameters exceeds the + * limit of {@link java.lang.classfile##u1 u1} */ static MethodParametersAttribute of(MethodParameterInfo... parameters) { return of(List.of(parameters)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java index 678c0f29714..874a3eae4f2 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java @@ -171,7 +171,8 @@ public sealed interface ModuleAttribute * @param uses the consumed services * @param provides the provided services * @throws IllegalArgumentException if {@code moduleFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or any of the collections has a size + * over the limit of {@link java.lang.classfile##u2 u2} */ static ModuleAttribute of(ModuleEntry moduleName, int moduleFlags, Utf8Entry moduleVersion, @@ -188,6 +189,9 @@ public sealed interface ModuleAttribute * * @param moduleName the module name * @param attrHandler a handler that receives a {@link ModuleAttributeBuilder} + * @throws IllegalArgumentException if the information from the handler exceeds + * the {@code class} file format limit, such as a list with size + * over the limit of {@link java.lang.classfile##u2 u2} */ static ModuleAttribute of(ModuleDesc moduleName, Consumer attrHandler) { @@ -296,6 +300,8 @@ public sealed interface ModuleAttribute * @param exportsFlagsMask the export flags * @param exportsToModules the modules to export to, or empty for an unqualified export * @return this builder + * @throws IllegalArgumentException if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ ModuleAttributeBuilder exports(PackageDesc pkge, int exportsFlagsMask, ModuleDesc... exportsToModules); @@ -307,7 +313,9 @@ public sealed interface ModuleAttribute * @param exportsToModules the modules to export to, or empty for an unqualified export * @return this builder * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_EXPORTS} location + * {@link AccessFlag.Location#MODULE_EXPORTS} location or the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ default ModuleAttributeBuilder exports(PackageDesc pkge, Collection exportsFlags, ModuleDesc... exportsToModules) { return exports(pkge, Util.flagsToBits(AccessFlag.Location.MODULE_EXPORTS, exportsFlags), exportsToModules); @@ -333,6 +341,8 @@ public sealed interface ModuleAttribute * @param opensFlagsMask the open package flags * @param opensToModules the modules to open to, or empty for an unqualified open * @return this builder + * @throws IllegalArgumentException if the number of modules exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ ModuleAttributeBuilder opens(PackageDesc pkge, int opensFlagsMask, ModuleDesc... opensToModules); @@ -349,7 +359,9 @@ public sealed interface ModuleAttribute * @param opensToModules the modules to open to, or empty for an unqualified open * @return this builder * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_OPENS} location + * {@link AccessFlag.Location#MODULE_OPENS} location, or if the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ default ModuleAttributeBuilder opens(PackageDesc pkge, Collection opensFlags, ModuleDesc... opensToModules) { return opens(pkge, Util.flagsToBits(AccessFlag.Location.MODULE_OPENS, opensFlags), opensToModules); @@ -391,7 +403,10 @@ public sealed interface ModuleAttribute * @param service the service class provided * @param implClasses the implementation classes * @return this builder - * @throws IllegalArgumentException if {@code service} or any of the {@code implClasses} represents a primitive type + * @throws IllegalArgumentException if {@code service} or any of the + * {@code implClasses} represents a primitive type, or the + * number of implementations exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ ModuleAttributeBuilder provides(ClassDesc service, ClassDesc... implClasses); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleExportInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleExportInfo.java index af04c83d260..09751817316 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleExportInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleExportInfo.java @@ -104,7 +104,8 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if {@code exportFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageEntry exports, int exportFlags, List exportsTo) { @@ -119,7 +120,9 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_EXPORTS} location + * {@link AccessFlag.Location#MODULE_EXPORTS} location, or if the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageEntry exports, Collection exportFlags, List exportsTo) { @@ -134,7 +137,8 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if {@code exportFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageEntry exports, int exportFlags, @@ -150,7 +154,9 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_EXPORTS} location + * {@link AccessFlag.Location#MODULE_EXPORTS} location, or if the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageEntry exports, Collection exportFlags, @@ -166,7 +172,8 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if {@code exportFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageDesc exports, int exportFlags, List exportsTo) { @@ -183,7 +190,9 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_EXPORTS} location + * {@link AccessFlag.Location#MODULE_EXPORTS} location, or if the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageDesc exports, Collection exportFlags, List exportsTo) { @@ -198,7 +207,8 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if {@code exportFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageDesc exports, int exportFlags, @@ -214,7 +224,9 @@ public sealed interface ModuleExportInfo * @param exportsTo the modules to which this package is exported, or empty * if this is an unqualified export * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_EXPORTS} location + * {@link AccessFlag.Location#MODULE_EXPORTS} location, or if the + * number of modules exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static ModuleExportInfo of(PackageDesc exports, Collection exportFlags, diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleHashesAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleHashesAttribute.java index 11c016aa9e1..30951753529 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleHashesAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleHashesAttribute.java @@ -96,6 +96,8 @@ public sealed interface ModuleHashesAttribute * {@return a {@code ModuleHashes} attribute} * @param algorithm the hashing algorithm * @param hashes the hash descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleHashesAttribute of(String algorithm, List hashes) { @@ -106,6 +108,8 @@ public sealed interface ModuleHashesAttribute * {@return a {@code ModuleHashes} attribute} * @param algorithm the hashing algorithm * @param hashes the hash descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleHashesAttribute of(String algorithm, ModuleHashInfo... hashes) { @@ -116,6 +120,8 @@ public sealed interface ModuleHashesAttribute * {@return a {@code ModuleHashes} attribute} * @param algorithm the hashing algorithm * @param hashes the hash descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleHashesAttribute of(Utf8Entry algorithm, List hashes) { @@ -126,6 +132,8 @@ public sealed interface ModuleHashesAttribute * {@return a {@code ModuleHashes} attribute} * @param algorithm the hashing algorithm * @param hashes the hash descriptions + * @throws IllegalArgumentException if the number of descriptions exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleHashesAttribute of(Utf8Entry algorithm, ModuleHashInfo... hashes) { diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleOpenInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleOpenInfo.java index 40a9b929776..3bccabcc907 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleOpenInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleOpenInfo.java @@ -110,7 +110,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if {@code opensFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageEntry opens, int opensFlags, List opensTo) { @@ -125,7 +126,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_OPENS} location + * {@link AccessFlag.Location#MODULE_OPENS} location, or the number + * of modules exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageEntry opens, Collection opensFlags, List opensTo) { @@ -140,7 +142,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if {@code opensFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageEntry opens, int opensFlags, @@ -156,7 +159,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_OPENS} location + * {@link AccessFlag.Location#MODULE_OPENS} location, or the number + * of modules exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageEntry opens, Collection opensFlags, @@ -171,7 +175,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, if it is a * qualified open, or empty * @throws IllegalArgumentException if {@code opensFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageDesc opens, int opensFlags, List opensTo) { @@ -187,7 +192,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the modules to which this package is opened, if it is a * qualified open, or empty * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_OPENS} location + * {@link AccessFlag.Location#MODULE_OPENS} location, or the number + * of modules exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageDesc opens, Collection opensFlags, List opensTo) { @@ -201,7 +207,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the packages to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if {@code opensFlags} is not {@link - * java.lang.classfile##u2 u2} + * java.lang.classfile##u2 u2} or if the number of modules exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageDesc opens, int opensFlags, @@ -216,7 +223,8 @@ public sealed interface ModuleOpenInfo * @param opensTo the packages to which this package is opened, or empty if * this is an unqualified open * @throws IllegalArgumentException if any flag cannot be applied to the - * {@link AccessFlag.Location#MODULE_OPENS} location + * {@link AccessFlag.Location#MODULE_OPENS} location, or the number + * of modules exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleOpenInfo of(PackageDesc opens, Collection opensFlags, diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModulePackagesAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModulePackagesAttribute.java index 24a9218c9fd..147e3ec9d1f 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModulePackagesAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModulePackagesAttribute.java @@ -74,6 +74,8 @@ public sealed interface ModulePackagesAttribute /** * {@return a {@code ModulePackages} attribute} * @param packages the packages + * @throws IllegalArgumentException if the number of packages exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ModulePackagesAttribute of(List packages) { return new UnboundAttribute.UnboundModulePackagesAttribute(packages); @@ -82,6 +84,8 @@ public sealed interface ModulePackagesAttribute /** * {@return a {@code ModulePackages} attribute} * @param packages the packages + * @throws IllegalArgumentException if the number of packages exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ModulePackagesAttribute of(PackageEntry... packages) { return of(List.of(packages)); @@ -90,6 +94,8 @@ public sealed interface ModulePackagesAttribute /** * {@return a {@code ModulePackages} attribute} * @param packages the packages + * @throws IllegalArgumentException if the number of packages exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ModulePackagesAttribute ofNames(List packages) { var p = new PackageEntry[packages.size()]; @@ -102,6 +108,8 @@ public sealed interface ModulePackagesAttribute /** * {@return a {@code ModulePackages} attribute} * @param packages the packages + * @throws IllegalArgumentException if the number of packages exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static ModulePackagesAttribute ofNames(PackageDesc... packages) { // List view, since ref to packages is temporary diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleProvideInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleProvideInfo.java index 76dc88c50f7..c3707597b53 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleProvideInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleProvideInfo.java @@ -64,6 +64,8 @@ public sealed interface ModuleProvideInfo * {@return a service provision description} * @param provides the service class interface * @param providesWith the service class implementations, must not be empty + * @throws IllegalArgumentException if the number of implementations exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleProvideInfo of(ClassEntry provides, List providesWith) { @@ -74,6 +76,8 @@ public sealed interface ModuleProvideInfo * {@return a service provision description} * @param provides the service class interface * @param providesWith the service class implementations, must not be empty + * @throws IllegalArgumentException if the number of implementations exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static ModuleProvideInfo of(ClassEntry provides, ClassEntry... providesWith) { @@ -85,7 +89,8 @@ public sealed interface ModuleProvideInfo * @param provides the service class interface * @param providesWith the service class implementations, must not be empty * @throws IllegalArgumentException if {@code provides} or any of {@code - * providesWith} represents a primitive type + * providesWith} represents a primitive type, or the number of + * implementations exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleProvideInfo of(ClassDesc provides, List providesWith) { @@ -97,7 +102,8 @@ public sealed interface ModuleProvideInfo * @param provides the service class interface * @param providesWith the service class implementations, must not be empty * @throws IllegalArgumentException if {@code provides} or any of {@code - * providesWith} represents a primitive type + * providesWith} represents a primitive type, or the number of + * implementations exceeds the limit of {@link java.lang.classfile##u2 u2} */ static ModuleProvideInfo of(ClassDesc provides, ClassDesc... providesWith) { diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/NestMembersAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/NestMembersAttribute.java index baab955221c..244b7e67bcd 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/NestMembersAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/NestMembersAttribute.java @@ -72,6 +72,8 @@ public sealed interface NestMembersAttribute extends Attribute nestMembers) { return new UnboundAttribute.UnboundNestMembersAttribute(nestMembers); @@ -81,6 +83,8 @@ public sealed interface NestMembersAttribute extends Attribute nestMembers) { return of(Util.entryList(nestMembers)); @@ -100,7 +106,9 @@ public sealed interface NestMembersAttribute extends Attribute permittedSubclasses) { return new UnboundAttribute.UnboundPermittedSubclassesAttribute(permittedSubclasses); @@ -86,6 +88,8 @@ public sealed interface PermittedSubclassesAttribute * {@return a {@code PermittedSubclasses} attribute} * * @param permittedSubclasses the permitted subclasses or subinterfaces + * @throws IllegalArgumentException if the number of permitted subclasses + * or subinterfaces exceeds the limit of {@link java.lang.classfile##u2 u2} */ static PermittedSubclassesAttribute of(ClassEntry... permittedSubclasses) { return of(List.of(permittedSubclasses)); @@ -95,7 +99,9 @@ public sealed interface PermittedSubclassesAttribute * {@return a {@code PermittedSubclasses} attribute} * * @param permittedSubclasses the permitted subclasses or subinterfaces - * @throws IllegalArgumentException if any of {@code permittedSubclasses} is primitive + * @throws IllegalArgumentException if any of {@code permittedSubclasses} is primitive, + * or if the number of permitted subclasses or subinterfaces exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static PermittedSubclassesAttribute ofSymbols(List permittedSubclasses) { return of(Util.entryList(permittedSubclasses)); @@ -105,7 +111,9 @@ public sealed interface PermittedSubclassesAttribute * {@return a {@code PermittedSubclasses} attribute} * * @param permittedSubclasses the permitted subclasses or subinterfaces - * @throws IllegalArgumentException if any of {@code permittedSubclasses} is primitive + * @throws IllegalArgumentException if any of {@code permittedSubclasses} is primitive, + * or if the number of permitted subclasses or subinterfaces exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static PermittedSubclassesAttribute ofSymbols(ClassDesc... permittedSubclasses) { // List version does defensive copy diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RecordAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RecordAttribute.java index 8f55b3a1cf9..f01765991d2 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RecordAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RecordAttribute.java @@ -68,6 +68,8 @@ public sealed interface RecordAttribute extends Attribute, Clas * {@return a {@code Record} attribute} * * @param components the record components + * @throws IllegalArgumentException if the number of record components + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static RecordAttribute of(List components) { return new UnboundAttribute.UnboundRecordAttribute(components); @@ -77,6 +79,8 @@ public sealed interface RecordAttribute extends Attribute, Clas * {@return a {@code Record} attribute} * * @param components the record components + * @throws IllegalArgumentException if the number of record components + * exceeds the limit of {@link java.lang.classfile##u2 u2} */ static RecordAttribute of(RecordComponentInfo... components) { return of(List.of(components)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RecordComponentInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/RecordComponentInfo.java index f3c74f066cf..23fb229ffd0 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RecordComponentInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RecordComponentInfo.java @@ -90,6 +90,8 @@ public sealed interface RecordComponentInfo * @param name the component name * @param descriptor the component field descriptor string * @param attributes the component attributes + * @throws IllegalArgumentException if the number of attributes exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RecordComponentInfo of(Utf8Entry name, Utf8Entry descriptor, @@ -103,6 +105,8 @@ public sealed interface RecordComponentInfo * @param name the component name * @param descriptor the component field descriptor sting * @param attributes the component attributes + * @throws IllegalArgumentException if the number of attributes exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RecordComponentInfo of(Utf8Entry name, Utf8Entry descriptor, @@ -116,6 +120,8 @@ public sealed interface RecordComponentInfo * @param name the component name * @param descriptor the component symbolic field descriptor * @param attributes the component attributes + * @throws IllegalArgumentException if the number of attributes exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RecordComponentInfo of(String name, ClassDesc descriptor, @@ -131,6 +137,8 @@ public sealed interface RecordComponentInfo * @param name the component name * @param descriptor the component symbolic field descriptor * @param attributes the component attributes + * @throws IllegalArgumentException if the number of attributes exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RecordComponentInfo of(String name, ClassDesc descriptor, diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleAnnotationsAttribute.java index da453ad4f5e..8a1e14280d3 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleAnnotationsAttribute.java @@ -73,6 +73,8 @@ public sealed interface RuntimeInvisibleAnnotationsAttribute * {@return a {@code RuntimeInvisibleAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static RuntimeInvisibleAnnotationsAttribute of(List annotations) { return new UnboundAttribute.UnboundRuntimeInvisibleAnnotationsAttribute(annotations); @@ -82,6 +84,8 @@ public sealed interface RuntimeInvisibleAnnotationsAttribute * {@return a {@code RuntimeInvisibleAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds + * the limit of {@link java.lang.classfile##u2 u2} */ static RuntimeInvisibleAnnotationsAttribute of(Annotation... annotations) { return of(List.of(annotations)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleParameterAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleParameterAnnotationsAttribute.java index 2051cd5dcdf..a721b9c1f9f 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleParameterAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleParameterAnnotationsAttribute.java @@ -86,6 +86,10 @@ public sealed interface RuntimeInvisibleParameterAnnotationsAttribute * some synthetic or implicit parameters. * * @param parameterAnnotations a list of run-time invisible annotations for each parameter + * @throws IllegalArgumentException if the number of parameters exceeds the + * limit of {@link java.lang.classfile##u1 u1}, or the number of + * annotations on any parameter exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static RuntimeInvisibleParameterAnnotationsAttribute of(List> parameterAnnotations) { return new UnboundAttribute.UnboundRuntimeInvisibleParameterAnnotationsAttribute(parameterAnnotations); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleTypeAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleTypeAnnotationsAttribute.java index 3ff1a643a82..15c90527c6a 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleTypeAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeInvisibleTypeAnnotationsAttribute.java @@ -79,6 +79,8 @@ public sealed interface RuntimeInvisibleTypeAnnotationsAttribute * {@return a {@code RuntimeInvisibleTypeAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeInvisibleTypeAnnotationsAttribute of(List annotations) { return new UnboundAttribute.UnboundRuntimeInvisibleTypeAnnotationsAttribute(annotations); @@ -88,6 +90,8 @@ public sealed interface RuntimeInvisibleTypeAnnotationsAttribute * {@return a {@code RuntimeInvisibleTypeAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeInvisibleTypeAnnotationsAttribute of(TypeAnnotation... annotations) { return of(List.of(annotations)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.java index ceabe2131af..db9cb96f4e0 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.java @@ -72,6 +72,8 @@ public sealed interface RuntimeVisibleAnnotationsAttribute /** * {@return a {@code RuntimeVisibleAnnotations} attribute} * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeVisibleAnnotationsAttribute of(List annotations) { return new UnboundAttribute.UnboundRuntimeVisibleAnnotationsAttribute(annotations); @@ -80,6 +82,8 @@ public sealed interface RuntimeVisibleAnnotationsAttribute /** * {@return a {@code RuntimeVisibleAnnotations} attribute} * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeVisibleAnnotationsAttribute of(Annotation... annotations) { return of(List.of(annotations)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleParameterAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleParameterAnnotationsAttribute.java index 2cf462d246c..4585efe5c65 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleParameterAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleParameterAnnotationsAttribute.java @@ -88,6 +88,10 @@ public sealed interface RuntimeVisibleParameterAnnotationsAttribute * some synthetic or implicit parameters. * * @param parameterAnnotations a list of run-time visible annotations for each parameter + * @throws IllegalArgumentException if the number of parameters exceeds the + * limit of {@link java.lang.classfile##u1 u1}, or the number of + * annotations on any parameter exceeds the limit of {@link + * java.lang.classfile##u2 u2} */ static RuntimeVisibleParameterAnnotationsAttribute of(List> parameterAnnotations) { return new UnboundAttribute.UnboundRuntimeVisibleParameterAnnotationsAttribute(parameterAnnotations); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleTypeAnnotationsAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleTypeAnnotationsAttribute.java index ad4595ffb6b..0476dc60b82 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleTypeAnnotationsAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/RuntimeVisibleTypeAnnotationsAttribute.java @@ -79,6 +79,8 @@ public sealed interface RuntimeVisibleTypeAnnotationsAttribute * {@return a {@code RuntimeVisibleTypeAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeVisibleTypeAnnotationsAttribute of(List annotations) { return new UnboundAttribute.UnboundRuntimeVisibleTypeAnnotationsAttribute(annotations); @@ -88,6 +90,8 @@ public sealed interface RuntimeVisibleTypeAnnotationsAttribute * {@return a {@code RuntimeVisibleTypeAnnotations} attribute} * * @param annotations the annotations + * @throws IllegalArgumentException if the number of annotations exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ static RuntimeVisibleTypeAnnotationsAttribute of(TypeAnnotation... annotations) { return of(List.of(annotations)); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index 71d5ccc79a4..06e9e6d585e 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java @@ -83,6 +83,8 @@ public sealed interface StackMapFrameInfo * @param target the location of the frame * @param locals the complete list of frame locals * @param stack the complete frame stack + * @throws IllegalArgumentException if the number of types in {@code locals} + * or {@code stack} exceeds the limit of {@link java.lang.classfile##u2 u2} */ public static StackMapFrameInfo of(Label target, List locals, diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapTableAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapTableAttribute.java index e9b3acbff5b..f30e8cb01a7 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapTableAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapTableAttribute.java @@ -75,6 +75,8 @@ public sealed interface StackMapTableAttribute * {@return a stack map table attribute} * * @param entries the stack map frames + * @throws IllegalArgumentException if the number of frames exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ public static StackMapTableAttribute of(List entries) { return new UnboundAttribute.UnboundStackMapTableAttribute(entries); diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java index 7d3f3c546a7..d10ff70120c 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java @@ -571,6 +571,8 @@ public sealed interface ConstantPoolBuilder * * @param methodReference the bootstrap method * @param arguments the arguments + * @throws IllegalArgumentException if the number of arguments exceeds the + * limit of {@link java.lang.classfile##u2 u2} */ default BootstrapMethodEntry bsmEntry(DirectMethodHandleDesc methodReference, List arguments) { @@ -586,6 +588,8 @@ public sealed interface ConstantPoolBuilder * * @param methodReference the {@code MethodHandleEntry} * @param arguments the list of {@code LoadableConstantEntry} + * @throws IllegalArgumentException if the number of arguments exceeds the + * limit of {@link java.lang.classfile##u2 u2} * @see BootstrapMethodEntry#bootstrapMethod() * BootstrapMethodEntry::bootstrapMethod * @see BootstrapMethodEntry#arguments() BootstrapMethodEntry::arguments diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java index 81a4e973d3f..1c87ff87a4d 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java @@ -54,7 +54,8 @@ import jdk.internal.classfile.impl.AbstractPoolEntry; * Unlike most constant pool entries, a UTF-8 entry is of flexible length: it is * represented as an array structure, with an {@code u2} for the data length in * bytes, followed by that number of bytes of Modified UTF-8 data. It can - * represent at most 65535 bytes of data due to the physical restrictions. + * represent at most 65535 bytes of data due to the physical restrictions of + * {@link java.lang.classfile##u2 u2}. * * @jvms 4.4.7 The {@code CONSTANT_Utf8_info} Structure * @see DataInput##modified-utf-8 Modified UTF-8 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 5d905aace65..da9ad7fbf0d 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 @@ -255,7 +255,7 @@ * 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.

+ * will cause a {@code NullPointerException}, unless otherwise specified. * *

Symbolic information

* To describe symbolic information for classes and types, the API uses the diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java index b75cf100a1b..6543b4e5079 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -40,7 +40,7 @@ public record AnnotationImpl(Utf8Entry className, List elemen implements Annotation { public AnnotationImpl { requireNonNull(className); - elements = List.copyOf(elements); + elements = Util.sanitizeU2List(elements); } @Override @@ -189,7 +189,7 @@ public record AnnotationImpl(Utf8Entry className, List elemen public record OfArrayImpl(List values) implements AnnotationValue.OfArray { public OfArrayImpl { - values = List.copyOf(values); + values = Util.sanitizeU2List(values); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java index fffb963eab3..d5437bc3a2c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -66,6 +66,7 @@ public class AttributeHolder { public void writeTo(BufWriterImpl buf) { int attributesCount = this.attributesCount; + Util.checkU2(attributesCount, "attributes count"); buf.writeU2(attributesCount); for (int i = 0; i < attributesCount; i++) { Util.writeAttribute(buf, attributes[i]); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java index ed36b5ce172..9919d8a1697 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -41,13 +41,13 @@ public final class BootstrapMethodEntryImpl implements BootstrapMethodEntry { private final List arguments; BootstrapMethodEntryImpl(ConstantPool constantPool, int bsmIndex, int hash, - MethodHandleEntryImpl handle, - List arguments) { + MethodHandleEntryImpl handle, + List arguments) { this.index = bsmIndex; this.hash = hash; this.constantPool = constantPool; this.handle = handle; - this.arguments = List.copyOf(arguments); + this.arguments = Util.sanitizeU2List(arguments); } @Override 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 40c5b1172b5..dda9accd8b9 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 @@ -276,9 +276,7 @@ public final class BufWriterImpl implements BufWriter { int strlen = str.length(); int countNonZeroAscii = JLA.countNonZeroAscii(str); int utflen = utfLen(str, countNonZeroAscii); - if (utflen > 65535) { - throw new IllegalArgumentException("string too long"); - } + Util.checkU2(utflen, "utf8 length"); reserveSpace(utflen + 3); int offset = this.offset; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 0ed4a3a9418..5bdbb571b68 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -185,14 +185,14 @@ public final class DirectCodeBuilder private void writeExceptionHandlers(BufWriterImpl buf) { int pos = buf.size(); int handlersSize = handlers.size(); + Util.checkU2(handlersSize, "exception handlers"); buf.writeU2(handlersSize); if (handlersSize > 0) { - writeExceptionHandlers(buf, pos); + writeExceptionHandlers(buf, pos, handlersSize); } } - private void writeExceptionHandlers(BufWriterImpl buf, int pos) { - int handlersSize = handlers.size(); + private void writeExceptionHandlers(BufWriterImpl buf, int pos, int handlersSize) { for (AbstractPseudoInstruction.ExceptionCatchImpl h : handlers) { int startPc = labelToBci(h.tryStart()); int endPc = labelToBci(h.tryEnd()); @@ -227,6 +227,7 @@ public final class DirectCodeBuilder public void writeBody(BufWriterImpl b) { int pos = b.size(); int crSize = characterRangesCount; + Util.checkU2(crSize, "character range count"); b.writeU2(crSize); for (int i = 0; i < characterRangesCount; i++) { CharacterRange cr = characterRanges[i]; @@ -262,6 +263,7 @@ public final class DirectCodeBuilder public void writeBody(BufWriterImpl b) { int pos = b.size(); int lvSize = localVariablesCount; + Util.checkU2(lvSize, "local variable count"); b.writeU2(lvSize); for (int i = 0; i < localVariablesCount; i++) { LocalVariable l = localVariables[i]; @@ -291,6 +293,7 @@ public final class DirectCodeBuilder public void writeBody(BufWriterImpl b) { int pos = b.size(); int lvtSize = localVariableTypesCount; + Util.checkU2(lvtSize, "local variable type count"); b.writeU2(lvtSize); for (int i = 0; i < localVariableTypesCount; i++) { LocalVariableType l = localVariableTypes[i]; @@ -441,7 +444,7 @@ public final class DirectCodeBuilder b.writeIndex(b.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE)); push(); b.writeInt(buf.size() + 2); - b.writeU2(buf.size() / 4); + b.writeU2(Util.checkU2(buf.size() / 4, "line number count")); b.writeBytes(buf); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/InterfacesImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/InterfacesImpl.java index d27b5e20ab4..79c84e4e66f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/InterfacesImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/InterfacesImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -35,7 +35,7 @@ public final class InterfacesImpl private final List interfaces; public InterfacesImpl(List interfaces) { - this.interfaces = List.copyOf(interfaces); + this.interfaces = Util.sanitizeU2List(interfaces); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index d9ca3ff61bc..a82a9577225 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -129,6 +129,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder { if (bsmSize == 0) return false; int pos = buf.size(); + Util.checkU2(bsmSize, "num bootstrap methods"); if (parent != null && parentBsmSize != 0) { parent.writeBootstrapMethods(buf); for (int i = parentBsmSize; i < bsmSize; i++) @@ -160,15 +161,14 @@ public final class SplitConstantPool implements ConstantPoolBuilder { void writeTo(BufWriterImpl buf) { int writeFrom = 1; - if (size() >= 65536) { - throw new IllegalArgumentException(String.format("Constant pool is too large %d", size())); - } - buf.writeU2(size()); + int mySize = size(); + Util.checkU2(mySize, "constant pool count"); + buf.writeU2(mySize); if (parent != null && buf.constantPool().canWriteDirect(this)) { parent.writeConstantPoolEntries(buf); writeFrom = parent.size(); } - for (int i = writeFrom; i < size(); ) { + for (int i = writeFrom; i < mySize; ) { var info = (AbstractPoolEntry) entryByIndex(i); info.writeTo(buf); i += info.width(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index f8e58ed2242..c5ce2204c09 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -303,8 +303,8 @@ public class StackMapDecoder { implements StackMapFrameInfo { public StackMapFrameImpl { requireNonNull(target); - locals = List.copyOf(locals); - stack = List.copyOf(stack); + locals = Util.sanitizeU2List(locals); + stack = Util.sanitizeU2List(stack); } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java index a0afb5efae1..62f0e2eab16 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java @@ -108,7 +108,7 @@ public final class TargetInfoImpl { public LocalVarTargetImpl(TargetType targetType, List table) { this.targetType = checkValid(targetType, TARGET_LOCAL_VARIABLE, TARGET_RESOURCE_VARIABLE); - this.table = List.copyOf(table); + this.table = Util.sanitizeU2List(table); } @Override public int size() { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java index 347b0c12657..94ba782d808 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java @@ -174,7 +174,7 @@ public abstract sealed class UnboundAttribute> public UnboundExceptionsAttribute(List exceptions) { super(Attributes.exceptions()); - this.exceptions = List.copyOf(exceptions); + this.exceptions = Util.sanitizeU2List(exceptions); } @Override @@ -244,7 +244,7 @@ public abstract sealed class UnboundAttribute> public UnboundStackMapTableAttribute(List entries) { super(Attributes.stackMapTable()); - this.entries = List.copyOf(entries); + this.entries = Util.sanitizeU2List(entries); } @Override @@ -268,7 +268,7 @@ public abstract sealed class UnboundAttribute> public UnboundInnerClassesAttribute(List innerClasses) { super(Attributes.innerClasses()); - this.innerClasses = List.copyOf(innerClasses); + this.innerClasses = Util.sanitizeU2List(innerClasses); } @Override @@ -292,7 +292,7 @@ public abstract sealed class UnboundAttribute> public UnboundRecordAttribute(List components) { super(Attributes.record()); - this.components = List.copyOf(components); + this.components = Util.sanitizeU2List(components); } @Override @@ -347,7 +347,7 @@ public abstract sealed class UnboundAttribute> public UnboundMethodParametersAttribute(List parameters) { super(Attributes.methodParameters()); - this.parameters = List.copyOf(parameters); + this.parameters = Util.sanitizeU1List(parameters); } @Override @@ -421,7 +421,7 @@ public abstract sealed class UnboundAttribute> public UnboundModuleHashesAttribute(Utf8Entry algorithm, List hashes) { super(Attributes.moduleHashes()); this.algorithm = requireNonNull(algorithm); - this.hashes = List.copyOf(hashes); + this.hashes = Util.sanitizeU2List(hashes); } @Override @@ -446,16 +446,16 @@ public abstract sealed class UnboundAttribute> private static final Utf8Entry NAME = TemporaryConstantPool.INSTANCE.utf8Entry(Attributes.NAME_MODULE_PACKAGES); - private final Collection packages; + private final List packages; public UnboundModulePackagesAttribute(Collection packages) { super(Attributes.modulePackages()); - this.packages = List.copyOf(packages); + this.packages = Util.sanitizeU2List(packages); } @Override public List packages() { - return List.copyOf(packages); + return packages; } @Override @@ -498,7 +498,7 @@ public abstract sealed class UnboundAttribute> public UnboundPermittedSubclassesAttribute(List permittedSubclasses) { super(Attributes.permittedSubclasses()); - this.permittedSubclasses = List.copyOf(permittedSubclasses); + this.permittedSubclasses = Util.sanitizeU2List(permittedSubclasses); } @Override @@ -522,7 +522,7 @@ public abstract sealed class UnboundAttribute> public UnboundNestMembersAttribute(List memberEntries) { super(Attributes.nestMembers()); - this.memberEntries = List.copyOf(memberEntries); + this.memberEntries = Util.sanitizeU2List(memberEntries); } @Override @@ -642,7 +642,7 @@ public abstract sealed class UnboundAttribute> public UnboundCharacterRangeTableAttribute(List ranges) { super(Attributes.characterRangeTable()); - this.ranges = List.copyOf(ranges); + this.ranges = Util.sanitizeU2List(ranges); } @Override @@ -666,7 +666,7 @@ public abstract sealed class UnboundAttribute> public UnboundLineNumberTableAttribute(List lines) { super(Attributes.lineNumberTable()); - this.lines = List.copyOf(lines); + this.lines = Util.sanitizeU2List(lines); } @Override @@ -690,7 +690,7 @@ public abstract sealed class UnboundAttribute> public UnboundLocalVariableTableAttribute(List locals) { super(Attributes.localVariableTable()); - this.locals = List.copyOf(locals); + this.locals = Util.sanitizeU2List(locals); } @Override @@ -714,7 +714,7 @@ public abstract sealed class UnboundAttribute> public UnboundLocalVariableTypeTableAttribute(List locals) { super(Attributes.localVariableTypeTable()); - this.locals = List.copyOf(locals); + this.locals = Util.sanitizeU2List(locals); } @Override @@ -738,7 +738,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeVisibleAnnotationsAttribute(List elements) { super(Attributes.runtimeVisibleAnnotations()); - this.elements = List.copyOf(elements); + this.elements = Util.sanitizeU2List(elements); } @Override @@ -762,7 +762,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeInvisibleAnnotationsAttribute(List elements) { super(Attributes.runtimeInvisibleAnnotations()); - this.elements = List.copyOf(elements); + this.elements = Util.sanitizeU2List(elements); } @Override @@ -786,13 +786,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeVisibleParameterAnnotationsAttribute(List> elements) { super(Attributes.runtimeVisibleParameterAnnotations()); - // deep copy - var array = elements.toArray().clone(); - for (int i = 0; i < array.length; i++) { - array[i] = List.copyOf((List) array[i]); - } - - this.elements = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); + this.elements = Util.sanitizeParameterAnnotations(elements); } @Override @@ -816,13 +810,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeInvisibleParameterAnnotationsAttribute(List> elements) { super(Attributes.runtimeInvisibleParameterAnnotations()); - // deep copy - var array = elements.toArray().clone(); - for (int i = 0; i < array.length; i++) { - array[i] = List.copyOf((List) array[i]); - } - - this.elements = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); + this.elements = Util.sanitizeParameterAnnotations(elements); } @Override @@ -846,7 +834,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeVisibleTypeAnnotationsAttribute(List elements) { super(Attributes.runtimeVisibleTypeAnnotations()); - this.elements = List.copyOf(elements); + this.elements = Util.sanitizeU2List(elements); } @Override @@ -870,7 +858,7 @@ public abstract sealed class UnboundAttribute> public UnboundRuntimeInvisibleTypeAnnotationsAttribute(List elements) { super(Attributes.runtimeInvisibleTypeAnnotations()); - this.elements = List.copyOf(elements); + this.elements = Util.sanitizeU2List(elements); } @Override @@ -955,7 +943,7 @@ public abstract sealed class UnboundAttribute> public UnboundModuleExportInfo { requireNonNull(exportedPackage); Util.checkFlags(exportsFlagsMask); - exportsTo = List.copyOf(exportsTo); + exportsTo = Util.sanitizeU2List(exportsTo); } } @@ -973,7 +961,7 @@ public abstract sealed class UnboundAttribute> public UnboundModuleOpenInfo { requireNonNull(openedPackage); Util.checkFlags(opensFlagsMask); - opensTo = List.copyOf(opensTo); + opensTo = Util.sanitizeU2List(opensTo); } } @@ -982,7 +970,7 @@ public abstract sealed class UnboundAttribute> implements ModuleProvideInfo { public UnboundModuleProvideInfo { requireNonNull(provides); - providesWith = List.copyOf(providesWith); + providesWith = Util.sanitizeU2List(providesWith); } } @@ -1003,7 +991,7 @@ public abstract sealed class UnboundAttribute> public UnboundRecordComponentInfo { requireNonNull(name); requireNonNull(descriptor); - attributes = List.copyOf(attributes); + attributes = Util.sanitizeU2List(attributes); } } @@ -1013,7 +1001,7 @@ public abstract sealed class UnboundAttribute> public UnboundTypeAnnotation { requireNonNull(targetInfo); - targetPath = List.copyOf(targetPath); + targetPath = Util.sanitizeU1List(targetPath); requireNonNull(annotation); } } @@ -1047,11 +1035,11 @@ public abstract sealed class UnboundAttribute> this.moduleName = requireNonNull(moduleName); this.moduleFlags = Util.checkFlags(moduleFlags); this.moduleVersion = moduleVersion; - this.requires = List.copyOf(requires); - this.exports = List.copyOf(exports); - this.opens = List.copyOf(opens); - this.uses = List.copyOf(uses); - this.provides = List.copyOf(provides); + this.requires = Util.sanitizeU2List(requires); + this.exports = Util.sanitizeU2List(exports); + this.opens = Util.sanitizeU2List(opens); + this.uses = Util.sanitizeU2List(uses); + this.provides = Util.sanitizeU2List(provides); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 0eea2bffd22..7e6384dd1a4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -146,6 +146,33 @@ public final class Util { : ClassDesc.ofInternalName(classInternalNameOrArrayDesc); } + /// Sanitizes an input list to make it immutable, and verify its size can + /// be represented with U1, throwing IAE otherwise. + public static List sanitizeU1List(List input) { + var copy = List.copyOf(input); + checkU1(copy.size(), "list size"); + return copy; + } + + /// Sanitizes an input list to make it immutable, and verify its size can + /// be represented with U2, throwing IAE otherwise. + public static List sanitizeU2List(Collection input) { + var copy = List.copyOf(input); + checkU2(copy.size(), "list size"); + return copy; + } + + /// Sanitizes an input nested list of parameter annotations. + public static List> sanitizeParameterAnnotations(List> input) { + var array = input.toArray().clone(); + checkU1(array.length, "parameter count"); + for (int i = 0; i < array.length; i++) { + array[i] = sanitizeU2List((List) array[i]); + } + + return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); + } + public static List mappedList(List list, Function mapper) { return new AbstractList<>() { @Override @@ -259,6 +286,7 @@ public final class Util { @ForceInline public static void writeAttributes(BufWriterImpl buf, List> list) { int size = list.size(); + Util.checkU2(size, "attributes count"); buf.writeU2(size); for (int i = 0; i < size; i++) { writeAttribute(buf, list.get(i)); @@ -267,6 +295,7 @@ public final class Util { @ForceInline static void writeList(BufWriterImpl buf, Writable[] array, int size) { + Util.checkU2(size, "member count"); buf.writeU2(size); for (int i = 0; i < size; i++) { array[i].writeTo(buf); diff --git a/test/jdk/jdk/classfile/LimitsTest.java b/test/jdk/jdk/classfile/LimitsTest.java index 6f3dc04c665..0b347c835db 100644 --- a/test/jdk/jdk/classfile/LimitsTest.java +++ b/test/jdk/jdk/classfile/LimitsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -23,17 +23,22 @@ /* * @test - * @bug 8320360 8330684 8331320 8331655 8331940 8332486 8335820 8336833 + * @bug 8320360 8330684 8331320 8331655 8331940 8332486 8335820 8336833 8361635 * @summary Testing ClassFile limits. * @run junit LimitsTest */ + +import java.lang.classfile.AttributeMapper; +import java.lang.classfile.AttributedElement; import java.lang.classfile.Attributes; -import java.lang.classfile.constantpool.PoolEntry; -import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; +import java.lang.classfile.BufWriter; import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassReader; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CustomAttribute; +import java.lang.classfile.Label; import java.lang.classfile.Opcode; +import java.lang.classfile.Signature; import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.LineNumberInfo; import java.lang.classfile.attribute.LineNumberTableAttribute; @@ -41,8 +46,16 @@ import java.lang.classfile.attribute.LocalVariableTableAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.ConstantPoolException; import java.lang.classfile.constantpool.IntegerEntry; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.classfile.instruction.SwitchCase; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectCodeBuilder; @@ -51,6 +64,8 @@ import jdk.internal.classfile.impl.LabelContext; import jdk.internal.classfile.impl.UnboundAttribute; import org.junit.jupiter.api.Test; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.*; import static org.junit.jupiter.api.Assertions.*; class LimitsTest { @@ -73,6 +88,114 @@ class LimitsTest { })); } + @Test + void testBsmOverLimit() { + AtomicBoolean reached = new AtomicBoolean(); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> { + var cp = clb.constantPool(); + var mhe = cp.methodHandleEntry(BSM_GET_STATIC_FINAL); + var digits = new IntegerEntry[10]; + for (int i = 0; i < 10; i++) { + digits[i] = cp.intEntry(i); + } + int lastIndex = -1; + for (int i = 0; i < 66000; i++) { + lastIndex = cp.bsmEntry(mhe, List.of( + digits[i / 10000 % 10], + digits[i / 1000 % 10], + digits[i / 100 % 10], + digits[i / 10 % 10], + digits[i / 1 % 10])).bsmIndex(); + } + assertEquals(65999, lastIndex); + reached.set(true); + })); + assertTrue(reached.get()); + } + + @Test + void testTooManyFields() { + AtomicBoolean reached = new AtomicBoolean(); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> { + for (int i = 1; i < 66000; i++) { + clb.withField("f", CD_int, 0); + } + reached.set(true); + })); + assertTrue(reached.get()); + } + + @Test + void testTooManyMethods() { + AtomicBoolean reached = new AtomicBoolean(); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> { + for (int i = 1; i < 66000; i++) { + clb.withMethodBody("m", MTD_void, 0, CodeBuilder::return_); + } + reached.set(true); + })); + assertTrue(reached.get()); + } + + static final class MyAttribute extends CustomAttribute { + static final MyAttribute INSTANCE = new MyAttribute(); + + private enum Mapper implements AttributeMapper { + INSTANCE; + + @Override + public MyAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) { + throw new UnsupportedOperationException(); + } + + @Override + public void writeAttribute(BufWriter buf, MyAttribute attr) { + buf.writeIndex(buf.constantPool().utf8Entry("MyAttribute")); + buf.writeInt(0); + } + + @Override + public boolean allowMultiple() { + return true; + } + + @Override + public AttributeStability stability() { + return AttributeStability.STATELESS; + } + + + } + + private MyAttribute() { + super(Mapper.INSTANCE); + } + } + + @Test + void testTooManyClassAttributes() { + AtomicBoolean reached = new AtomicBoolean(); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> { + for (int i = 1; i < 66000; i++) { + clb.with(MyAttribute.INSTANCE); + } + reached.set(true); + })); + assertTrue(reached.get()); + } + + @Test + void testTooManyFieldAttributes() { + AtomicBoolean reached = new AtomicBoolean(); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> clb.withField("f", CD_int, fb -> { + for (int i = 1; i < 66000; i++) { + fb.with(MyAttribute.INSTANCE); + } + reached.set(true); + }))); + assertTrue(reached.get()); + } + @Test void testCodeOverLimit() { assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(ClassDesc.of("BigClass"), cb -> cb.withMethodBody( @@ -99,6 +222,91 @@ class LimitsTest { assertThrows(IllegalArgumentException.class, () -> lc.getLabel(10)); } + private static void testPseudoOverflow(BiConsumer handler) { + ClassFile cf = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS); + AtomicBoolean reached = new AtomicBoolean(false); + assertDoesNotThrow(() -> cf.build(CD_Void, cb -> cb.withMethodBody("test", MTD_void, ACC_STATIC, cob -> { + cob.nop(); + var label = cob.newLabel(); + for (int i = 0; i < 65535; i++) { + handler.accept(cob, label); + } + cob.labelBinding(label); + cob.return_(); + reached.set(true); + }))); + assertTrue(reached.get()); + + reached.set(false); + assertThrows(IllegalArgumentException.class, () -> cf.build(CD_Void, cb -> cb.withMethodBody("test", MTD_void, ACC_STATIC, cob -> { + cob.nop(); + var label = cob.newLabel(); + for (int i = 0; i < 65536; i++) { + handler.accept(cob, label); + } + cob.labelBinding(label); + cob.return_(); + reached.set(true); + }))); + assertTrue(reached.get()); + } + + @Test + void testExceptionCatchOverflow() { + testPseudoOverflow((cob, label) -> cob.exceptionCatch(cob.startLabel(), label, label, CD_Throwable)); + } + + @Test + void testLocalVariableOverflow() { + testPseudoOverflow((cob, label) -> cob.localVariable(0, "fake", CD_int, cob.startLabel(), label)); + } + + @Test + void testLocalVariableTypeOverflow() { + testPseudoOverflow((cob, label) -> cob.localVariableType(0, "fake", Signature.of(CD_int), cob.startLabel(), label)); + } + + @Test + void testCharacterRangeOverflow() { + testPseudoOverflow((cob, label) -> cob.characterRange(cob.startLabel(), label, 0, 0, 0)); + } + + // LineNumber deduplicates so cannot really overflow + + @Test + void testHugeLookupswitch() { + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> clb.withMethodBody("test", MTD_void, ACC_STATIC, cob -> { + var l = cob.newLabel(); + // 10000 * 8 > 65535 + var cases = new ArrayList(10000); + for (int i = 0; i < 10000; i++) { + cases.add(SwitchCase.of(i, l)); + } + cob.lookupswitch(l, cases); + cob.labelBinding(l); + cob.return_(); + }))); + } + + @Test + void testHugeTableswitch() { + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> clb.withMethodBody("test", MTD_void, ACC_STATIC, cob -> { + var l = cob.newLabel(); + // 20000 * 4 > 65535 + cob.tableswitch(-10000, 10000, l, List.of()); + cob.labelBinding(l); + cob.return_(); + }))); + } + + @Test + void testHugeUtf8Entry() { + var longString = String.valueOf((char) 0x800).repeat(22000); + assertThrows(IllegalArgumentException.class, () -> ClassFile.of().build(CD_Void, clb -> { + clb.constantPool().utf8Entry(longString); + })); + } + @Test void testSupportedClassVersion() { var cf = ClassFile.of(); diff --git a/test/jdk/jdk/classfile/ListValidationTest.java b/test/jdk/jdk/classfile/ListValidationTest.java new file mode 100644 index 00000000000..831bff3a7b4 --- /dev/null +++ b/test/jdk/jdk/classfile/ListValidationTest.java @@ -0,0 +1,348 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test + * @bug 8361635 + * @summary Testing list size validation in class file format. + * @run junit ListValidationTest + */ + +import java.lang.classfile.Annotation; +import java.lang.classfile.AnnotationElement; +import java.lang.classfile.AnnotationValue; +import java.lang.classfile.ClassFile; +import java.lang.classfile.Interfaces; +import java.lang.classfile.Label; +import java.lang.classfile.TypeAnnotation; +import java.lang.classfile.attribute.*; +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.constant.ModuleDesc; +import java.lang.constant.PackageDesc; +import java.util.List; +import java.util.Optional; + +import jdk.internal.classfile.impl.TemporaryConstantPool; +import jdk.internal.classfile.impl.UnboundAttribute; +import org.junit.jupiter.api.Test; + +import static java.lang.constant.ConstantDescs.*; +import static java.util.Collections.nCopies; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ListValidationTest { + @Test + void testAnnotationElements() { + var e = AnnotationElement.ofInt("dummy", 0); + assertDoesNotThrow(() -> Annotation.of(CD_String, nCopies(65535, e))); + assertThrows(IllegalArgumentException.class, () -> Annotation.of(CD_String, nCopies(66000, e))); + } + + @Test + void testAnnotationArrayValue() { + var v = AnnotationValue.ofInt(0); + assertDoesNotThrow(() -> AnnotationValue.ofArray(nCopies(65535, v))); + assertThrows(IllegalArgumentException.class, () -> AnnotationValue.ofArray(nCopies(66000, v))); + } + + @Test + void testTypeAnnotationPath() { + var anno = Annotation.of(CD_String); + assertDoesNotThrow(() -> TypeAnnotation.of(TypeAnnotation.TargetInfo.ofField(), nCopies(255, TypeAnnotation.TypePathComponent.INNER_TYPE), anno)); + assertThrows(IllegalArgumentException.class, () -> TypeAnnotation.of(TypeAnnotation.TargetInfo.ofField(), nCopies(256, TypeAnnotation.TypePathComponent.INNER_TYPE), anno)); + } + + @Test + void testBsmArgs() { + var cpb = ConstantPoolBuilder.of(); + assertDoesNotThrow(() -> cpb.bsmEntry(BSM_INVOKE, nCopies(65535, 0))); + assertThrows(IllegalArgumentException.class, () -> cpb.bsmEntry(BSM_INVOKE, nCopies(66000, 0))); + } + + @Test + void testInterfaces() { + var cpb = ConstantPoolBuilder.of(); + assertDoesNotThrow(() -> Interfaces.ofSymbols(nCopies(65535, CD_Number))); + assertThrows(IllegalArgumentException.class, () -> cpb.bsmEntry(BSM_INVOKE, nCopies(66000, 0))); + } + + @Test + void testStackMapFrame() { + Label label = dummyLabel(); + assertDoesNotThrow(() -> StackMapFrameInfo.of(label, + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER), + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE))); + assertThrows(IllegalArgumentException.class, () -> StackMapFrameInfo.of(label, + nCopies(66000, StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER), + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE))); + assertThrows(IllegalArgumentException.class, () -> StackMapFrameInfo.of(label, + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER), + nCopies(66000, StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE))); + } + + @Test + void testTypeAnnotationLocalVarTarget() { + Label label = dummyLabel(); + assertDoesNotThrow(() -> TypeAnnotation.TargetInfo.ofLocalVariable(nCopies(65535, TypeAnnotation.LocalVarTargetInfo.of(label, label, 0)))); + assertThrows(IllegalArgumentException.class, () -> TypeAnnotation.TargetInfo.ofLocalVariable(nCopies(66000, TypeAnnotation.LocalVarTargetInfo.of(label, label, 0)))); + } + + @Test + void testExceptionsAttribute() { + assertDoesNotThrow(() -> ExceptionsAttribute.ofSymbols(nCopies(65535, CD_Throwable))); + assertThrows(IllegalArgumentException.class, () -> ExceptionsAttribute.ofSymbols(nCopies(66000, CD_Throwable))); + } + + @Test + void testStackMapTableAttribute() { + var frame = StackMapFrameInfo.of(dummyLabel(), + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER), + nCopies(65535, StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE)); + assertDoesNotThrow(() -> StackMapTableAttribute.of(nCopies(65535, frame))); + assertThrows(IllegalArgumentException.class, () -> StackMapTableAttribute.of(nCopies(66000, frame))); + } + + @Test + void testInnerClassesAttribute() { + var entry = InnerClassInfo.of(CD_Void, Optional.empty(), Optional.empty(), 0); + assertDoesNotThrow(() -> InnerClassesAttribute.of(nCopies(65535, entry))); + assertThrows(IllegalArgumentException.class, () -> InnerClassesAttribute.of(nCopies(66000, entry))); + } + + @Test + void testRecordAttribute() { + var component = RecordComponentInfo.of("hello", CD_int, List.of()); + assertDoesNotThrow(() -> RecordAttribute.of(nCopies(65535, component))); + assertThrows(IllegalArgumentException.class, () -> RecordAttribute.of(nCopies(66000, component))); + } + + @Test + void testMethodParametersAttribute() { + var component = MethodParameterInfo.of(Optional.empty(), 0); + assertDoesNotThrow(() -> MethodParametersAttribute.of(nCopies(255, component))); + assertThrows(IllegalArgumentException.class, () -> MethodParametersAttribute.of(nCopies(300, component))); + } + + @Test + void testModuleHashesAttribute() { + var hash = ModuleHashInfo.of(ModuleDesc.of("java.base"), new byte[0]); + assertDoesNotThrow(() -> ModuleHashesAttribute.of("dummy", nCopies(65535, hash))); + assertThrows(IllegalArgumentException.class, () -> ModuleHashesAttribute.of("dummy", nCopies(66000, hash))); + } + + @Test + void testModulePackagesAttribute() { + var pkgDesc = PackageDesc.of("java.io"); + assertDoesNotThrow(() -> ModulePackagesAttribute.ofNames(nCopies(65535, pkgDesc))); + assertThrows(IllegalArgumentException.class, () -> ModulePackagesAttribute.ofNames(nCopies(66000, pkgDesc))); + } + + @Test + void testPermittedSubclassesAttribute() { + assertDoesNotThrow(() -> PermittedSubclassesAttribute.ofSymbols(nCopies(65535, CD_Collection))); + assertThrows(IllegalArgumentException.class, () -> PermittedSubclassesAttribute.ofSymbols(nCopies(66000, CD_Collection))); + } + + @Test + void testNestMembersAttribute() { + assertDoesNotThrow(() -> NestMembersAttribute.ofSymbols(nCopies(65535, CD_Collection))); + assertThrows(IllegalArgumentException.class, () -> NestMembersAttribute.ofSymbols(nCopies(66000, CD_Collection))); + } + + @Test + void testCharacterRangeTableAttribute() { + var range = CharacterRangeInfo.of(0, 0, 0, 0, 0); + assertDoesNotThrow(() -> CharacterRangeTableAttribute.of(nCopies(65535, range))); + assertThrows(IllegalArgumentException.class, () -> CharacterRangeTableAttribute.of(nCopies(66000, range))); + } + + @Test + void testLineNumberTableAttribute() { + var lineNumber = LineNumberInfo.of(0, 0); + assertDoesNotThrow(() -> LineNumberTableAttribute.of(nCopies(65535, lineNumber))); + assertThrows(IllegalArgumentException.class, () -> LineNumberTableAttribute.of(nCopies(66000, lineNumber))); + } + + @Test + void testLocalVariableTableAttribute() { + var utf8 = TemporaryConstantPool.INSTANCE.utf8Entry("dummy"); + var localVariable = new UnboundAttribute.UnboundLocalVariableInfo(0, 0, utf8, utf8, 0); + assertDoesNotThrow(() -> LocalVariableTableAttribute.of(nCopies(65535, localVariable))); + assertThrows(IllegalArgumentException.class, () -> LocalVariableTableAttribute.of(nCopies(66000, localVariable))); + } + + @Test + void testLocalVariableTypeTableAttribute() { + var utf8 = TemporaryConstantPool.INSTANCE.utf8Entry("dummy"); + var localVariableType = new UnboundAttribute.UnboundLocalVariableTypeInfo(0, 0, utf8, utf8, 0); + assertDoesNotThrow(() -> LocalVariableTypeTableAttribute.of(nCopies(65535, localVariableType))); + assertThrows(IllegalArgumentException.class, () -> LocalVariableTypeTableAttribute.of(nCopies(66000, localVariableType))); + } + + @Test + void testRuntimeVisibleAnnotationsAttribute() { + var anno = Annotation.of(CD_String); + assertDoesNotThrow(() -> RuntimeVisibleAnnotationsAttribute.of(nCopies(65535, anno))); + assertThrows(IllegalArgumentException.class, () -> RuntimeVisibleAnnotationsAttribute.of(nCopies(66000, anno))); + } + + @Test + void testRuntimeInvisibleAnnotationsAttribute() { + var anno = Annotation.of(CD_String); + assertDoesNotThrow(() -> RuntimeInvisibleAnnotationsAttribute.of(nCopies(65535, anno))); + assertThrows(IllegalArgumentException.class, () -> RuntimeInvisibleAnnotationsAttribute.of(nCopies(66000, anno))); + } + + @Test + void testRuntimeVisibleParameterAnnotationsAttributeTopLevel() { + assertDoesNotThrow(() -> RuntimeVisibleParameterAnnotationsAttribute.of(nCopies(255, List.of()))); + assertThrows(IllegalArgumentException.class, () -> RuntimeVisibleParameterAnnotationsAttribute.of(nCopies(256, List.of()))); + } + + @Test + void testRuntimeInvisibleParameterAnnotationsAttributeTopLevel() { + assertDoesNotThrow(() -> RuntimeInvisibleParameterAnnotationsAttribute.of(nCopies(255, List.of()))); + assertThrows(IllegalArgumentException.class, () -> RuntimeInvisibleParameterAnnotationsAttribute.of(nCopies(256, List.of()))); + } + + @Test + void testRuntimeVisibleParameterAnnotationsAttributeNested() { + var anno = Annotation.of(CD_String); + assertDoesNotThrow(() -> RuntimeVisibleParameterAnnotationsAttribute.of(List.of(nCopies(65535, anno)))); + assertThrows(IllegalArgumentException.class, () -> RuntimeVisibleParameterAnnotationsAttribute.of(List.of(nCopies(65536, anno)))); + } + + @Test + void testRuntimeInvisibleParameterAnnotationsAttributeNested() { + var anno = Annotation.of(CD_String); + assertDoesNotThrow(() -> RuntimeInvisibleParameterAnnotationsAttribute.of(List.of(nCopies(65535, anno)))); + assertThrows(IllegalArgumentException.class, () -> RuntimeInvisibleParameterAnnotationsAttribute.of(List.of(nCopies(65536, anno)))); + } + + @Test + void testRuntimeVisibleTypeAnnotationsAttribute() { + var anno = TypeAnnotation.of(TypeAnnotation.TargetInfo.ofMethodReturn(), List.of(), Annotation.of(CD_String)); + assertDoesNotThrow(() -> RuntimeVisibleTypeAnnotationsAttribute.of(nCopies(65535, anno))); + assertThrows(IllegalArgumentException.class, () -> RuntimeVisibleTypeAnnotationsAttribute.of(nCopies(66000, anno))); + } + + @Test + void testRuntimeInvisibleTypeAnnotationsAttribute() { + var anno = TypeAnnotation.of(TypeAnnotation.TargetInfo.ofMethodReturn(), List.of(), Annotation.of(CD_String)); + assertDoesNotThrow(() -> RuntimeInvisibleTypeAnnotationsAttribute.of(nCopies(65535, anno))); + assertThrows(IllegalArgumentException.class, () -> RuntimeInvisibleTypeAnnotationsAttribute.of(nCopies(66000, anno))); + } + + @Test + void testModuleExportEntry() { + var pkg = PackageDesc.of("dummy.test"); + var mod = ModuleDesc.of("the.other"); + assertDoesNotThrow(() -> ModuleExportInfo.of(pkg, 0, nCopies(65535, mod))); + assertThrows(IllegalArgumentException.class, () -> ModuleExportInfo.of(pkg, 0, nCopies(66000, mod))); + } + + @Test + void testModuleOpenEntry() { + var pkg = PackageDesc.of("dummy.test"); + var mod = ModuleDesc.of("the.other"); + assertDoesNotThrow(() -> ModuleOpenInfo.of(pkg, 0, nCopies(65535, mod))); + assertThrows(IllegalArgumentException.class, () -> ModuleOpenInfo.of(pkg, 0, nCopies(66000, mod))); + } + + @Test + void testModuleProvideEntry() { + assertDoesNotThrow(() -> ModuleProvideInfo.of(CD_Object, nCopies(65535, CD_String))); + assertThrows(IllegalArgumentException.class, () -> ModuleProvideInfo.of(CD_Object, nCopies(66000, CD_String))); + } + + @Test + void testRecordComponentAttributes() { + var attr = SyntheticAttribute.of(); + assertDoesNotThrow(() -> RecordComponentInfo.of("dummy", CD_int, nCopies(65535, attr))); + assertThrows(IllegalArgumentException.class, () -> RecordComponentInfo.of("dummy", CD_int, nCopies(66000, attr))); + } + + @Test + void testModuleAttribute() { + var md = ModuleDesc.of("java.base"); + var pkg = PackageDesc.of("java.lang"); + var require = ModuleRequireInfo.of(md, 0, null); + var export = ModuleExportInfo.of(pkg, 0, List.of()); + var provide = ModuleProvideInfo.of(CD_Object, List.of()); + var open = ModuleOpenInfo.of(pkg, 0, List.of()); + var classEntry = TemporaryConstantPool.INSTANCE.classEntry(CD_String); + var moduleEntry = TemporaryConstantPool.INSTANCE.moduleEntry(md); + assertDoesNotThrow(() -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(65535, require), + nCopies(65535, export), + nCopies(65535, open), + nCopies(65535, classEntry), + nCopies(65535, provide) + )); + assertThrows(IllegalArgumentException.class, () -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(66000, require), + nCopies(65535, export), + nCopies(65535, open), + nCopies(65535, classEntry), + nCopies(65535, provide) + )); + assertThrows(IllegalArgumentException.class, () -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(65535, require), + nCopies(66000, export), + nCopies(65535, open), + nCopies(65535, classEntry), + nCopies(65535, provide) + )); + assertThrows(IllegalArgumentException.class, () -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(65535, require), + nCopies(65535, export), + nCopies(66000, open), + nCopies(65535, classEntry), + nCopies(65535, provide) + )); + assertThrows(IllegalArgumentException.class, () -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(65535, require), + nCopies(65535, export), + nCopies(65535, open), + nCopies(66000, classEntry), + nCopies(65535, provide) + )); + assertThrows(IllegalArgumentException.class, () -> ModuleAttribute.of(moduleEntry, 0, null, + nCopies(65535, require), + nCopies(65535, export), + nCopies(65535, open), + nCopies(65535, classEntry), + nCopies(66000, provide) + )); + } + + private static Label dummyLabel() { + Label[] capture = new Label[1]; + ClassFile.of().build(CD_Object, clb -> clb.withMethodBody("test", MTD_void, 0, cob -> { + capture[0] = cob.startLabel(); + cob.return_(); + })); + return capture[0]; + } +}