diff --git a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java index b1d026f52e4c4..33db731efc80c 100644 --- a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java +++ b/src/java.base/share/classes/java/lang/classfile/AccessFlags.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 @@ -30,10 +30,33 @@ import jdk.internal.classfile.impl.AccessFlagsImpl; /** - * Models the access flags for a class, method, or field. Delivered as a - * {@link ClassElement}, {@link FieldElement}, or {@link MethodElement} - * when traversing the corresponding model type. + * Models the access flags for a class, method, or field. The access flags + * appears exactly once in each class, method, or field; a {@link + * ClassBuilder} and a {@link FieldBuilder} chooses an unspecified default value + * if access flags are not provided, and a {@link MethodBuilder} is always + * created with access flags. + *

+ * {@code AccessFlags} cannot be created via a factory method directly; it can + * be created with {@code withFlags} methods on the respective builders. + *

+ * A {@link MethodBuilder} throws an {@link IllegalArgumentException} if it is + * supplied an {@code AccessFlags} object that changes the preexisting + * {@link ClassFile#ACC_STATIC ACC_STATIC} flag of the builder, because the + * access flag change may invalidate previously supplied data to the builder. * + * @apiNote + * The access flags of classes, methods, and fields are modeled as a standalone + * object to support streaming as elements for {@link ClassFileTransform}. + * Other access flags are not elements of a {@link CompoundElement} and thus not + * modeled by {@code AccessFlags}; they provide their own {@code flagsMask}, + * {@code flags}, and {@code has} methods. + * + * @see ClassModel#flags() + * @see FieldModel#flags() + * @see MethodModel#flags() + * @see ClassBuilder#withFlags + * @see FieldBuilder#withFlags + * @see MethodBuilder#withFlags * @since 24 */ public sealed interface AccessFlags @@ -41,26 +64,36 @@ public sealed interface AccessFlags permits AccessFlagsImpl { /** - * {@return the access flags, as a bit mask} + * {@return the access flags, as a bit mask} It is in the range of unsigned + * short, {@code [0, 0xFFFF]}. */ int flagsMask(); /** - * {@return the access flags} + * {@return the access flags, as a set of flag enums} + * + * @throws IllegalArgumentException if the flags mask has any undefined bit set + * @see #location() */ Set flags(); /** - * {@return whether the specified flag is present} The specified flag - * should be a valid flag for the classfile location associated with this - * element otherwise false is returned. + * {@return whether the specified flag is set} If the specified flag + * is not available to this {@linkplain #location() location}, returns + * {@code false}. + * * @param flag the flag to test + * @see #location() */ boolean has(AccessFlag flag); /** - * {@return the classfile location for this element, which is either class, - * method, or field} + * {@return the {@code class} file location for this element, which is + * either class, method, or field} + * + * @see AccessFlag.Location#CLASS + * @see AccessFlag.Location#FIELD + * @see AccessFlag.Location#METHOD */ AccessFlag.Location location(); } 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 54a9ed80573ce..ae31fdf3967bb 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -54,7 +54,7 @@ public sealed interface AnnotationValue { /** * Models an annotation value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_ANNOTATION}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_ANNOTATION}. * * @since 24 */ @@ -66,7 +66,7 @@ sealed interface OfAnnotation extends AnnotationValue /** * Models an array value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_ARRAY}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_ARRAY}. * * @since 24 */ @@ -122,7 +122,7 @@ sealed interface OfConstant extends AnnotationValue { /** * Models a string value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_STRING}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_STRING}. * * @since 24 */ @@ -149,7 +149,7 @@ default String resolvedValue() { /** * Models a double value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_DOUBLE}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_DOUBLE}. * * @since 24 */ @@ -176,7 +176,7 @@ default Double resolvedValue() { /** * Models a float value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_FLOAT}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_FLOAT}. * * @since 24 */ @@ -203,7 +203,7 @@ default Float resolvedValue() { /** * Models a long value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_LONG}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_LONG}. * * @since 24 */ @@ -230,7 +230,7 @@ default Long resolvedValue() { /** * Models an int value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_INT}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_INT}. * * @since 24 */ @@ -257,7 +257,7 @@ default Integer resolvedValue() { /** * Models a short value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_SHORT}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_SHORT}. * * @since 24 */ @@ -287,7 +287,7 @@ default Short resolvedValue() { /** * Models a char value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_CHAR}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_CHAR}. * * @since 24 */ @@ -317,7 +317,7 @@ default Character resolvedValue() { /** * Models a byte value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_BYTE}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_BYTE}. * * @since 24 */ @@ -347,7 +347,7 @@ default Byte resolvedValue() { /** * Models a boolean value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_BOOLEAN}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_BOOLEAN}. * * @since 24 */ @@ -377,7 +377,7 @@ default Boolean resolvedValue() { /** * Models a class value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_CLASS}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_CLASS}. * * @since 24 */ @@ -394,7 +394,7 @@ default ClassDesc classSymbol() { /** * Models an enum value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value TAG_ENUM}. + * The {@linkplain #tag tag} of this value is {@value %c TAG_ENUM}. * * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java index 0edb79a863d91..1feef1ba35e4f 100644 --- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java @@ -39,7 +39,14 @@ * A {@link ClassFileElement} describing a {@code class} file structure that has * attributes, such as a {@code class} file, a field, a method, a {@link * CodeAttribute Code} attribute, or a record component. + *

+ * Unless otherwise specified, most attributes that can be discovered in a + * {@link CompoundElement} implements the corresponding {@linkplain + * ClassFileElement##membership membership subinterface} of {@code + * ClassFileElement}, and can be sent to a {@link ClassFileBuilder} to be + * integrated into the built structure. * + * @see java.lang.classfile.attribute * @jvms 4.7 Attributes * @sealedGraph * @since 24 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 996c63ddd0126..6efd240d8af73 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.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 @@ -25,8 +25,8 @@ package java.lang.classfile; -import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; @@ -41,14 +41,19 @@ import jdk.internal.classfile.impl.Util; /** - * A builder for classfiles. Builders are not created directly; they are passed - * to handlers by methods such as {@link ClassFile#build(ClassDesc, Consumer)} - * or to class transforms. The elements of a classfile can be specified - * abstractly (by passing a {@link ClassElement} to {@link #with(ClassFileElement)}) - * or concretely by calling the various {@code withXxx} methods. + * A builder for a {@code class} file. {@link ClassFile} provides different + * {@code build} methods that accept handlers to configure such a builder; + * {@link ClassFile#build(ClassDesc, Consumer)} suffices for basic usage, while + * {@link ClassFile#build(ClassEntry, ConstantPoolBuilder, Consumer)} allows + * fine-grained control over {@linkplain ClassFileBuilder#constantPool() the + * constant pool}. + *

+ * Refer to {@link ClassFileBuilder} for general guidance and caution around + * the use of builders for structures in the {@code class} file format. * + * @see ClassFile#build(ClassEntry, ConstantPoolBuilder, Consumer) + * @see ClassModel * @see ClassTransform - * * @since 24 */ public sealed interface ClassBuilder @@ -56,28 +61,38 @@ public sealed interface ClassBuilder permits ChainedClassBuilder, DirectClassBuilder { /** - * Sets the classfile version. + * Sets the version of this class. + * * @param major the major version number * @param minor the minor version number * @return this builder + * @see ClassFileVersion */ default ClassBuilder withVersion(int major, int minor) { return with(ClassFileVersion.of(major, minor)); } /** - * Sets the classfile access flags. + * Sets the access flags of this class. + * * @param flags the access flags, as a bit mask * @return this builder + * @see AccessFlags + * @see AccessFlag.Location#CLASS */ default ClassBuilder withFlags(int flags) { return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); } /** - * Sets the classfile access flags. - * @param flags the access flags + * Sets the access flags of this class. + * + * @param flags the access flags, as flag enums * @return this builder + * @throws IllegalArgumentException if any flag cannot be applied to the + * {@link AccessFlag.Location#CLASS} location + * @see AccessFlags + * @see AccessFlag.Location#CLASS */ default ClassBuilder withFlags(AccessFlag... flags) { return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); @@ -85,8 +100,10 @@ default ClassBuilder withFlags(AccessFlag... flags) { /** * Sets the superclass of this class. + * * @param superclassEntry the superclass * @return this builder + * @see Superclass */ default ClassBuilder withSuperclass(ClassEntry superclassEntry) { return with(Superclass.of(superclassEntry)); @@ -94,9 +111,11 @@ default ClassBuilder withSuperclass(ClassEntry superclassEntry) { /** * Sets the superclass of this class. + * * @param desc the superclass * @return this builder * @throws IllegalArgumentException if {@code desc} represents a primitive type + * @see Superclass */ default ClassBuilder withSuperclass(ClassDesc desc) { return withSuperclass(constantPool().classEntry(desc)); @@ -104,8 +123,10 @@ default ClassBuilder withSuperclass(ClassDesc desc) { /** * Sets the interfaces of this class. + * * @param interfaces the interfaces * @return this builder + * @see Interfaces */ default ClassBuilder withInterfaces(List interfaces) { return with(Interfaces.of(interfaces)); @@ -113,8 +134,10 @@ default ClassBuilder withInterfaces(List interfaces) { /** * Sets the interfaces of this class. + * * @param interfaces the interfaces * @return this builder + * @see Interfaces */ default ClassBuilder withInterfaces(ClassEntry... interfaces) { return withInterfaces(List.of(interfaces)); @@ -122,8 +145,11 @@ default ClassBuilder withInterfaces(ClassEntry... interfaces) { /** * Sets the interfaces of this class. + * * @param interfaces the interfaces * @return this builder + * @throws IllegalArgumentException if any element of {@code interfaces} is primitive + * @see Interfaces */ default ClassBuilder withInterfaceSymbols(List interfaces) { return withInterfaces(Util.entryList(interfaces)); @@ -131,32 +157,39 @@ default ClassBuilder withInterfaceSymbols(List interfaces) { /** * Sets the interfaces of this class. + * * @param interfaces the interfaces * @return this builder + * @throws IllegalArgumentException if any element of {@code interfaces} is primitive + * @see Interfaces */ default ClassBuilder withInterfaceSymbols(ClassDesc... interfaces) { - // List view, since ref to interfaces is temporary + // list version does defensive copy return withInterfaceSymbols(Arrays.asList(interfaces)); } /** * Adds a field. - * @param name the name of the field - * @param descriptor the field descriptor - * @param handler handler which receives a {@link FieldBuilder} which can - * further define the contents of the field + * + * @param name the field name + * @param descriptor the field descriptor string + * @param handler handler to supply the contents of the field * @return this builder + * @see FieldModel */ ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer handler); /** - * Adds a field. - * @param name the name of the field - * @param descriptor the field descriptor - * @param flags the access flags for this field + * Adds a field, with only access flags. + * + * @param name the field name + * @param descriptor the field descriptor string + * @param flags the access flags for this field, as a bit mask * @return this builder + * @see FieldModel + * @see FieldBuilder#withFlags(int) */ default ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, @@ -166,11 +199,12 @@ default ClassBuilder withField(Utf8Entry name, /** * Adds a field. - * @param name the name of the field - * @param descriptor the field descriptor - * @param handler handler which receives a {@link FieldBuilder} which can - * further define the contents of the field + * + * @param name the field name + * @param descriptor the symbolic field descriptor + * @param handler handler to supply the contents of the field * @return this builder + * @see FieldModel */ default ClassBuilder withField(String name, ClassDesc descriptor, @@ -181,11 +215,14 @@ default ClassBuilder withField(String name, } /** - * Adds a field. - * @param name the name of the field - * @param descriptor the field descriptor - * @param flags the access flags for this field + * Adds a field, with only access flags. + * + * @param name the field name + * @param descriptor the symbolic field descriptor + * @param flags the access flags for this field, as a bit mask * @return this builder + * @see FieldModel + * @see FieldBuilder#withFlags(int) */ default ClassBuilder withField(String name, ClassDesc descriptor, @@ -197,28 +234,33 @@ default ClassBuilder withField(String name, /** * Adds a field by transforming a field from another class. - * - * @implNote - *

This method behaves as if: + *

+ * This method behaves as if: * {@snippet lang=java : - * withField(field.fieldName(), field.fieldType(), - * b -> b.transformField(field, transform)); + * // @link substring=withField target="#withField(Utf8Entry, Utf8Entry, Consumer)" : + * withField(field.fieldName(), field.fieldType(), + * fb -> fb.transform(field, transform)) // @link regex="transform(?=\()" target="FieldBuilder#transform" * } * * @param field the field to be transformed * @param transform the transform to apply to the field * @return this builder + * @see FieldTransform */ ClassBuilder transformField(FieldModel field, FieldTransform transform); /** - * Adds a method. - * @param name the name of the method + * Adds a method. The bit for {@link ClassFile#ACC_STATIC ACC_STATIC} flag + * cannot be modified by the {@code handler} later, and must be set through + * {@code methodFlags}. + * + * @param name the method name * @param descriptor the method descriptor - * @param methodFlags the access flags - * @param handler handler which receives a {@link MethodBuilder} which can - * further define the contents of the method + * @param methodFlags the access flags as a bit mask, with the {@code + * ACC_STATIC} bit definitely set + * @param handler handler to supply the contents of the method * @return this builder + * @see MethodModel */ ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, @@ -226,14 +268,23 @@ ClassBuilder withMethod(Utf8Entry name, Consumer handler); /** - * Adds a method, with only a {@code Code} attribute. + * Adds a method, with only access flags and a {@link CodeModel}. The bit + * for {@link ClassFile#ACC_STATIC ACC_STATIC} flag cannot be modified by + * the {@code handler} later, and must be set through {@code methodFlags}. + *

+ * This method behaves as if: + * {@snippet lang=java : + * // @link substring=withMethod target="#withMethod(Utf8Entry, Utf8Entry, int, Consumer)" : + * withMethod(name, descriptor, methodFlags, mb -> mb.withCode(handler)) // @link substring=withCode target="MethodBuilder#withCode" + * } * - * @param name the name of the method + * @param name the method name * @param descriptor the method descriptor - * @param methodFlags the access flags - * @param handler handler which receives a {@link CodeBuilder} which can - * define the contents of the method body + * @param methodFlags the access flags as a bit mask, with the {@code + * ACC_STATIC} bit definitely set + * @param handler handler to supply the contents of the method body * @return this builder + * @see MethodModel */ default ClassBuilder withMethodBody(Utf8Entry name, Utf8Entry descriptor, @@ -243,13 +294,17 @@ default ClassBuilder withMethodBody(Utf8Entry name, } /** - * Adds a method. - * @param name the name of the method + * Adds a method. The bit for {@link ClassFile#ACC_STATIC ACC_STATIC} flag + * cannot be modified by the {@code handler}, and must be set through + * {@code methodFlags}. + * + * @param name the method name * @param descriptor the method descriptor - * @param methodFlags the access flags - * @param handler handler which receives a {@link MethodBuilder} which can - * further define the contents of the method + * @param methodFlags the access flags as a bit mask, with the {@code + * ACC_STATIC} bit definitely set + * @param handler handler to supply the contents of the method * @return this builder + * @see MethodModel */ default ClassBuilder withMethod(String name, MethodTypeDesc descriptor, @@ -262,13 +317,23 @@ default ClassBuilder withMethod(String name, } /** - * Adds a method, with only a {@link CodeAttribute}. - * @param name the name of the method + * Adds a method, with only access flags and a {@link CodeModel}. The bit + * for {@link ClassFile#ACC_STATIC ACC_STATIC} flag cannot be modified by + * the {@code handler}, and must be set through {@code methodFlags}. + *

+ * This method behaves as if: + * {@snippet lang=java : + * // @link substring=withMethod target="#withMethod(String, MethodTypeDesc, int, Consumer)" : + * withMethod(name, descriptor, methodFlags, mb -> mb.withCode(handler)) // @link substring=withCode target="MethodBuilder#withCode" + * } + * + * @param name the method name * @param descriptor the method descriptor - * @param methodFlags the access flags - * @param handler handler which receives a {@link CodeBuilder} which can - * define the contents of the method body + * @param methodFlags the access flags as a bit mask, with the {@code + * ACC_STATIC} bit definitely set + * @param handler handler to supply the contents of the method body * @return this builder + * @see MethodModel */ default ClassBuilder withMethodBody(String name, MethodTypeDesc descriptor, @@ -278,17 +343,21 @@ default ClassBuilder withMethodBody(String name, } /** - * Adds a method by transforming a method from another class. - * - * @implNote - *

This method behaves as if: + * Adds a method by transforming a method from another class. The transform + * cannot modify the {@link ClassFile#ACC_STATIC ACC_STATIC} flag of the + * original method. + *

+ * This method behaves as if: * {@snippet lang=java : - * withMethod(method.methodName(), method.methodType(), - * b -> b.transformMethod(method, transform)); + * // @link substring=withMethod target="#withMethod(Utf8Entry, Utf8Entry, int, Consumer)" : + * withMethod(method.methodName(), method.methodType(), method.flags().flagMask(), + * mb -> mb.transform(method, transform)) // @link regex="transform(?=\()" target="MethodBuilder#transform" * } + * * @param method the method to be transformed * @param transform the transform to apply to the method * @return this builder + * @see MethodTransform */ ClassBuilder transformMethod(MethodModel method, MethodTransform transform); } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassElement.java b/src/java.base/share/classes/java/lang/classfile/ClassElement.java index c39ab3c4a64d8..866d1bafcc4aa 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassElement.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassElement.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 @@ -27,9 +27,21 @@ import java.lang.classfile.attribute.*; /** - * A marker interface for elements that can appear when traversing - * a {@link ClassModel} or be presented to a {@link ClassBuilder}. + * Marker interface for a member element of a {@link ClassModel}. Such an + * element can appear when traversing a {@link ClassModel} unless otherwise + * specified, be supplied to a {@link ClassBuilder}, and be processed by a + * {@link ClassTransform}. + *

+ * {@link AccessFlags}, and {@link ClassFileVersion} are member elements of a + * class that appear exactly once during the traversal of a {@link ClassModel}. + * {@link Superclass} and {@link Interfaces} may be absent or appear at most + * once. A {@link ClassBuilder} may provide an alternative superclass if it is + * not defined but required. * + * @see ClassFileElement##membership Membership Elements + * @see MethodElement + * @see FieldElement + * @see CodeElement * @sealedGraph * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFile.java b/src/java.base/share/classes/java/lang/classfile/ClassFile.java index 61685a5c1e0ab..8268fa7a38145 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFile.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFile.java @@ -25,18 +25,27 @@ package java.lang.classfile; import java.io.IOException; +import java.lang.classfile.AttributeMapper.AttributeStability; import java.lang.classfile.attribute.CharacterRangeInfo; +import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.LocalVariableInfo; import java.lang.classfile.attribute.LocalVariableTypeInfo; import java.lang.classfile.attribute.ModuleAttribute; +import java.lang.classfile.attribute.StackMapTableAttribute; +import java.lang.classfile.attribute.UnknownAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.classfile.instruction.BranchInstruction; +import java.lang.classfile.instruction.CharacterRange; import java.lang.classfile.instruction.DiscontinuedInstruction; import java.lang.classfile.instruction.ExceptionCatch; +import java.lang.classfile.instruction.LineNumber; +import java.lang.classfile.instruction.LocalVariable; +import java.lang.classfile.instruction.LocalVariableType; import java.lang.constant.ClassDesc; import java.lang.reflect.AccessFlag; +import java.lang.reflect.ClassFileFormatVersion; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -50,9 +59,9 @@ import static jdk.internal.constant.ConstantUtils.CD_module_info; /** - * Represents a context for parsing, transforming, and generating classfiles. - * A {@code ClassFile} has a set of options that condition how parsing and - * generation is done. + * Provides ability to parse, transform, and generate {@code class} files. + * A {@code ClassFile} is a context with a set of options that condition how + * parsing and generation are done. * * @since 24 */ @@ -60,14 +69,20 @@ public sealed interface ClassFile permits ClassFileImpl { /** - * {@return a context with default options} + * {@return a context with default options} Each subtype of {@link Option} + * specifies its default. + *

+ * The default {@link AttributeMapperOption} and {@link + * ClassHierarchyResolverOption} may be unsuitable for some {@code class} + * files and result in parsing or generation errors. */ static ClassFile of() { return ClassFileImpl.DEFAULT_CONTEXT; } /** - * {@return a new context with options altered from the default} + * {@return a context with options altered from the default} Equivalent to + * {@link #of() ClassFile.of().withOptions(options)}. * @param options the desired processing options */ static ClassFile of(Option... options) { @@ -75,14 +90,15 @@ static ClassFile of(Option... options) { } /** - * {@return a copy of the context with altered options} + * {@return a context with altered options from this context} * @param options the desired processing options */ ClassFile withOptions(Option... options); /** - * An option that affects the parsing and writing of classfiles. + * An option that affects the parsing or writing of {@code class} files. * + * @see java.lang.classfile##options Options * @sealedGraph * @since 24 */ @@ -90,16 +106,32 @@ sealed interface Option { } /** - * Option describing attribute mappers for custom attributes. - * Default is only to process standard attributes. + * The option describing user-defined attributes for parsing {@code class} + * files. The default does not recognize any user-defined attribute. + *

+ * An {@code AttributeMapperOption} contains a function that maps an + * attribute name to a user attribute mapper. The function may return {@code + * null} if it does not recognize an attribute name. The returned mapper + * must ensure its {@link AttributeMapper#name() name()} is equivalent to + * the {@link Utf8Entry#stringValue() stringValue()} of the input {@link + * Utf8Entry}. + *

+ * The mapping function in this attribute has lower priority than mappers in + * {@link Attributes}, so it is impossible to override built-in attributes + * with this option. If an attribute is not recognized by any mapper in + * {@link Attributes} and is not assigned a mapper, or recognized, by this + * option, that attribute will be modeled by an {@link UnknownAttribute}. * + * @see AttributeMapper + * @see CustomAttribute * @since 24 */ sealed interface AttributeMapperOption extends Option permits ClassFileImpl.AttributeMapperOptionImpl { /** - * {@return an option describing attribute mappers for custom attributes} + * {@return an option describing user-defined attributes for parsing} + * * @param attributeMapper a function mapping attribute names to attribute mappers */ static AttributeMapperOption of(Function> attributeMapper) { @@ -114,17 +146,31 @@ static AttributeMapperOption of(Function> attribut } /** - * Option describing the class hierarchy resolver to use when generating - * stack maps. + * The option describing the class hierarchy resolver to use when generating + * stack maps or verifying classes. The default is {@link + * ClassHierarchyResolver#defaultResolver()}, which uses core reflection to + * find a class with a given name in {@linkplain ClassLoader#getSystemClassLoader() + * system class loader} and inspect it, and is insufficient if a class is + * not present in the system class loader as in applications, or if loading + * of system classes is not desired as in agents. + *

+ * A {@code ClassHierarchyResolverOption} contains a {@link ClassHierarchyResolver}. + * The resolver must be able to process all classes and interfaces, including + * those appearing as the component types of array types, that appear in the + * operand stack of the generated bytecode. If the resolver fails on any + * of the classes and interfaces with an {@link IllegalArgumentException}, + * the {@code class} file generation fails. * + * @see ClassHierarchyResolver + * @jvms 4.10.1.2 Verification Type System * @since 24 */ sealed interface ClassHierarchyResolverOption extends Option permits ClassFileImpl.ClassHierarchyResolverOptionImpl { /** - * {@return an option describing the class hierarchy resolver to use when - * generating stack maps} + * {@return an option describing the class hierarchy resolver to use} + * * @param classHierarchyResolver the resolver */ static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyResolver) { @@ -139,14 +185,22 @@ static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyReso } /** - * Option describing whether to preserve the original constant pool when - * transforming a {@code class} file. Reusing the constant pool enables - * significant optimizations in processing time and minimizes differences - * between the original and transformed {@code class} files, but may result - * in a bigger transformed {@code class} file when many elements of the - * original {@code class} file are dropped and many original constant - * pool entries become unused. Default is {@link #SHARED_POOL} to preserve - * the original constant pool. + * Option describing whether to extend from the original constant pool when + * transforming a {@code class} file. The default is {@link #SHARED_POOL} + * to extend from the original constant pool. + *

+ * This option affects all overloads of {@link #transformClass transformClass}. + * Extending from the original constant pool keeps the indices into the + * constant pool intact, which enables significant optimizations in processing + * time and minimizes differences between the original and transformed {@code + * class} files, but may result in a bigger transformed {@code class} file + * when many elements of the original {@code class} file are dropped and + * many original constant pool entries become unused. + *

+ * An alternative to this option is to use {@link #build(ClassEntry, + * ConstantPoolBuilder, Consumer)} directly. It allows extension from + * arbitrary constant pools, and may be useful if a built {@code class} file + * reuses structures from multiple original {@code class} files. * * @see ConstantPoolBuilder * @see #build(ClassEntry, ConstantPoolBuilder, Consumer) @@ -156,8 +210,8 @@ static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyReso enum ConstantPoolSharingOption implements Option { /** - * Preserves the original constant pool when transforming the {@code - * class} file. + * Extend the new constant pool from the original constant pool when + * transforming the {@code class} file. *

* These two transformations below are equivalent: * {@snippet lang=java : @@ -194,87 +248,143 @@ enum ConstantPoolSharingOption implements Option { } /** - * Option describing whether to patch out unreachable code. - * Default is {@code PATCH_DEAD_CODE} to automatically patch out unreachable - * code with NOPs. + * The option describing whether to patch out unreachable code for stack map + * generation. The default is {@link #PATCH_DEAD_CODE} to automatically + * patch unreachable code and generate a valid stack map entry for the + * patched code. + *

+ * The stack map generation process may fail when it encounters unreachable + * code and {@link #KEEP_DEAD_CODE} is set. In such cases, users should + * set {@link StackMapsOption#DROP_STACK_MAPS} and provide their own stack + * maps that passes verification (JVMS {@jvms 4.10.1}). * + * @see StackMapsOption + * @jvms 4.10.1 Verification by Type Checking * @since 24 */ enum DeadCodeOption implements Option { - /** Patch unreachable code */ + /** + * Patch unreachable code with dummy code, and generate valid dummy + * stack map entries. This ensures the generated code can pass + * verification (JVMS {@jvms 4.10.1}). + */ PATCH_DEAD_CODE, - /** Keep the unreachable code */ + /** + * Keep the unreachable code for the accuracy of the generated {@code + * class} file. Users should set {@link StackMapsOption#DROP_STACK_MAPS} + * to prevent stack map generation from running and provide their own + * {@link StackMapTableAttribute} to a {@link CodeBuilder}. + */ KEEP_DEAD_CODE } /** - * Option describing whether to filter unresolved labels. - * Default is {@code FAIL_ON_DEAD_LABELS} to throw IllegalArgumentException - * when any {@link ExceptionCatch}, {@link LocalVariableInfo}, - * {@link LocalVariableTypeInfo}, or {@link CharacterRangeInfo} - * reference to unresolved {@link Label} during bytecode serialization. - * Setting this option to {@code DROP_DEAD_LABELS} filters the above - * elements instead. + * The option describing whether to filter {@linkplain + * CodeBuilder#labelBinding(Label) unbound labels} and drop their + * enclosing structures if possible. The default is {@link + * #FAIL_ON_DEAD_LABELS} to fail fast with an {@link IllegalArgumentException} + * when a {@link PseudoInstruction} refers to an unbound label during + * bytecode generation. + *

+ * The affected {@link PseudoInstruction}s include {@link ExceptionCatch}, + * {@link LocalVariable}, {@link LocalVariableType}, and {@link + * CharacterRange}. Setting this option to {@link #DROP_DEAD_LABELS} + * filters these pseudo-instructions from a {@link CodeBuilder} instead. + * Note that instructions, such as {@link BranchInstruction}, with unbound + * labels always fail-fast with an {@link IllegalArgumentException}. * + * @see DebugElementsOption * @since 24 */ enum DeadLabelsOption implements Option { - /** Fail on unresolved labels */ + /** + * Fail fast on {@linkplain CodeBuilder#labelBinding(Label) unbound + * labels}. This also ensures the accuracy of the generated {@code + * class} files. + */ FAIL_ON_DEAD_LABELS, - /** Filter unresolved labels */ + /** + * Filter {@link PseudoInstruction}s with {@linkplain + * CodeBuilder#labelBinding(Label) unbound labels}. Note that + * instructions with unbound labels still cause an {@link + * IllegalArgumentException}. + */ DROP_DEAD_LABELS } /** - * Option describing whether to process or discard debug elements. - * Debug elements include the local variable table, local variable type - * table, and character range table. Discarding debug elements may - * reduce the overhead of parsing or transforming classfiles. - * Default is {@code PASS_DEBUG} to process debug elements. + * The option describing whether to process or discard debug {@link + * PseudoInstruction}s in the traversal of a {@link CodeModel} or a {@link + * CodeBuilder}. The default is {@link #PASS_DEBUG} to process debug + * pseudo-instructions as all other {@link CodeElement}. + *

+ * Debug pseudo-instructions include {@link LocalVariable}, {@link + * LocalVariableType}, and {@link CharacterRange}. Discarding debug + * elements may reduce the overhead of parsing or transforming {@code class} + * files and has no impact on the run-time behavior. * + * @see LineNumbersOption * @since 24 */ enum DebugElementsOption implements Option { - /** Process debug elements */ + /** + * Process debug pseudo-instructions like other member elements of a + * {@link CodeModel}. + */ PASS_DEBUG, - /** Drop debug elements */ + /** + * Drop debug pseudo-instructions from traversal and builders. + */ DROP_DEBUG } /** - * Option describing whether to process or discard line numbers. + * The option describing whether to process or discard {@link LineNumber}s + * in the traversal of a {@link CodeModel} or a {@link CodeBuilder}. The + * default is {@link #PASS_LINE_NUMBERS} to process all line number entries + * as all other {@link CodeElement}. + *

* Discarding line numbers may reduce the overhead of parsing or transforming - * classfiles. - * Default is {@code PASS_LINE_NUMBERS} to process line numbers. + * {@code class} files and has no impact on the run-time behavior. * + * @see DebugElementsOption * @since 24 */ enum LineNumbersOption implements Option { - /** Process line numbers */ + /** + * Process {@link LineNumber} like other member elements of a {@link + * CodeModel}. + */ PASS_LINE_NUMBERS, - /** Drop line numbers */ + /** + * Drop {@link LineNumber} from traversal and builders. + */ DROP_LINE_NUMBERS; } /** - * Option describing whether to automatically rewrite short jumps to - * long when necessary. - * Default is {@link #FIX_SHORT_JUMPS} to automatically rewrite jump - * instructions. + * The option describing whether to automatically rewrite short jumps to + * equivalent instructions when necessary. The default is {@link + * #FIX_SHORT_JUMPS} to automatically rewrite. *

* Due to physical restrictions, some types of instructions cannot encode * certain jump targets with bci offsets less than -32768 or greater than * 32767, as they use a {@code s2} to encode such an offset. (The maximum * length of the {@code code} array is 65535.) These types of instructions * are called "short jumps". + *

+ * Disabling rewrite can ensure the physical accuracy of a generated {@code + * class} file and avoid the overhead from a failed first attempt for + * overflowing forward jumps in some cases, if the generated {@code class} + * file is stable. * * @see BranchInstruction * @see DiscontinuedInstruction.JsrInstruction @@ -294,80 +404,153 @@ enum ShortJumpsOption implements Option { * Fail with an {@link IllegalArgumentException} if short jump overflows. *

* This is useful to ensure the physical accuracy of a generated {@code - * class} file. + * class} file and avoids the overhead from a failed first attempt for + * overflowing forward jumps in some cases. */ FAIL_ON_SHORT_JUMPS } /** - * Option describing whether to generate stackmaps. - * Default is {@code STACK_MAPS_WHEN_REQUIRED} to generate stack - * maps for {@link #JAVA_6_VERSION} or above, where specifically for - * {@link #JAVA_6_VERSION} the stack maps may not be generated. - * @jvms 4.10.1 Verification by Type Checking + * The option describing whether to generate stack maps. The default is + * {@link #STACK_MAPS_WHEN_REQUIRED} to generate stack maps or reuse + * existing ones if compatible. + *

+ * The {@link StackMapTableAttribute} is a derived property from a {@link + * CodeAttribute Code} attribute to allow a Java Virtual Machine to perform + * verification in one pass. Thus, it is not modeled as part of a {@link + * CodeModel}, but computed on-demand instead via stack maps generation. + *

+ * Stack map generation may fail with an {@link IllegalArgumentException} if + * there is {@linkplain DeadCodeOption unreachable code} or legacy + * {@linkplain DiscontinuedInstruction.JsrInstruction jump routine} + * instructions. When {@link #DROP_STACK_MAPS} option is used, users can + * provide their own stack maps by supplying a {@link StackMapTableAttribute} + * to a {@link CodeBuilder}. * + * @see StackMapTableAttribute + * @see DeadCodeOption + * @jvms 4.10.1 Verification by Type Checking * @since 24 */ enum StackMapsOption implements Option { - /** Generate stack maps when required */ + /** + * Generate stack maps or reuse existing ones if compatible. Stack maps + * are present on major versions {@value #JAVA_6_VERSION} or above. For + * these versions, {@link CodeBuilder} tries to reuse compatible stack + * maps information if the code array and exception handlers are still + * compatible after a transformation; otherwise, it runs stack map + * generation. However, it does not fail fast if the major version is + * {@value #JAVA_6_VERSION}, which allows jump subroutine instructions + * that are incompatible with stack maps to exist in the {@code code} + * array. + */ STACK_MAPS_WHEN_REQUIRED, - /** Always generate stack maps */ + /** + * Forces running stack map generation. This runs stack map generation + * unconditionally and fails fast if the generation fails due to any + * reason. + */ GENERATE_STACK_MAPS, - /** Drop stack maps from code */ + /** + * Do not run stack map generation. Users must supply their own + * {@link StackMapTableAttribute} to a {@link CodeBuilder} if the code + * has branches or exception handlers; otherwise, the generated code + * will fail verification (JVMS {@jvms 4.10.1}). + *

+ * This option is required for user-supplied {@link StackMapTableAttribute} + * to be respected. Stack maps on an existing {@link CodeAttribute Code} + * attribute can be reused as below with this option: + * {@snippet lang=java file="PackageSnippets.java" region="manual-reuse-stack-maps"} + */ DROP_STACK_MAPS } /** - * Option describing whether to process or discard unrecognized or problematic - * original attributes when a class, record component, field, method or code is - * transformed in its exploded form. - * Default is {@code PASS_ALL_ATTRIBUTES} to process all original attributes. - * @see AttributeMapper.AttributeStability + * The option describing whether to retain or discard attributes that cannot + * verify their correctness after a transformation. The default is {@link + * #PASS_ALL_ATTRIBUTES} to retain all attributes as-is. + *

+ * Many attributes only depend on data managed by the Class-File API, such + * as constant pool entries or labels into the {@code code} array. If they + * change, the Class-File API knows their updated values and can write a + * correct version by expanding the structures and recomputing the updated + * indexes, known as "explosion". However, some attributes, such as type + * annotations, depend on arbitrary data that may be modified during + * transformations but the Class-File API does not track, such as index to + * an entry in the {@linkplain ClassModel#interfaces() interfaces} of a + * {@code ClassFile} structure. As a result, the Class-File API cannot + * verify the correctness of such information. * + * @see AttributeStability * @since 24 */ enum AttributesProcessingOption implements Option { - /** Process all original attributes during transformation */ + /** + * Retain all original attributes during transformation. + */ PASS_ALL_ATTRIBUTES, - /** Drop unknown attributes during transformation */ + /** + * Drop attributes with {@link AttributeStability#UNKNOWN} data + * dependency during transformation. + */ DROP_UNKNOWN_ATTRIBUTES, - /** Drop unknown and unstable original attributes during transformation */ + /** + * Drop attributes with {@link AttributeStability#UNSTABLE} or higher + * data dependency during transformation. + */ DROP_UNSTABLE_ATTRIBUTES } /** - * Parse a classfile into a {@link ClassModel}. - * @param bytes the bytes of the classfile + * Parses a {@code class} file into a {@link ClassModel}. + *

+ * Due to the on-demand nature of {@code class} file parsing, an {@link + * IllegalArgumentException} may be thrown on any accessor method invocation + * on the returned model or any structure returned by the accessors in the + * structure hierarchy. + * + * @param bytes the bytes of the {@code class} file * @return the class model - * @throws IllegalArgumentException or its subclass if the classfile format is - * not supported or an incompatibility prevents parsing of the classfile + * @throws IllegalArgumentException if the {@code class} file is malformed + * or of a version {@linkplain #latestMajorVersion() not supported} + * by the current runtime */ ClassModel parse(byte[] bytes); /** - * Parse a classfile into a {@link ClassModel}. - * @param path the path to the classfile + * Parses a {@code class} into a {@link ClassModel}. + *

+ * Due to the on-demand nature of {@code class} file parsing, an {@link + * IllegalArgumentException} may be thrown on any accessor method invocation + * on the returned model or any structure returned by the accessors in the + * structure hierarchy. + * + * @param path the path to the {@code class} file * @return the class model - * @throws java.io.IOException if an I/O error occurs - * @throws IllegalArgumentException or its subclass if the classfile format is - * not supported or an incompatibility prevents parsing of the classfile + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if the {@code class} file is malformed + * or of a version {@linkplain #latestMajorVersion() not supported} + * by the current runtime + * @see #parse(byte[]) */ default ClassModel parse(Path path) throws IOException { return parse(Files.readAllBytes(path)); } /** - * Build a classfile into a byte array. + * Builds a {@code class} file into a byte array. + * * @param thisClass the name of the class to build * @param handler a handler that receives a {@link ClassBuilder} - * @return the classfile bytes - * @throws IllegalArgumentException if {@code thisClass} represents a primitive type + * @return the {@code class} file bytes + * @throws IllegalArgumentException if {@code thisClass} represents a + * primitive type or building encounters a failure */ default byte[] build(ClassDesc thisClass, Consumer handler) { @@ -376,24 +559,27 @@ default byte[] build(ClassDesc thisClass, } /** - * Build a classfile into a byte array using the provided constant pool - * builder. + * Builds a {@code class} file into a byte array using the provided constant + * pool builder. * * @param thisClassEntry the name of the class to build * @param constantPool the constant pool builder * @param handler a handler that receives a {@link ClassBuilder} - * @return the classfile bytes + * @return the {@code class} file bytes + * @throws IllegalArgumentException if building encounters a failure */ byte[] build(ClassEntry thisClassEntry, ConstantPoolBuilder constantPool, Consumer handler); /** - * Build a classfile into a file. + * Builds a {@code class} file into a file in a file system. + * * @param path the path to the file to write * @param thisClass the name of the class to build * @param handler a handler that receives a {@link ClassBuilder} - * @throws java.io.IOException if an I/O error occurs + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if building encounters a failure */ default void buildTo(Path path, ClassDesc thisClass, @@ -402,14 +588,15 @@ default void buildTo(Path path, } /** - * Build a classfile into a file using the provided constant pool - * builder. + * Builds a {@code class} file into a file in a file system using the + * provided constant pool builder. * * @param path the path to the file to write * @param thisClassEntry the name of the class to build * @param constantPool the constant pool builder * @param handler a handler that receives a {@link ClassBuilder} - * @throws java.io.IOException if an I/O error occurs + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if building encounters a failure */ default void buildTo(Path path, ClassEntry thisClassEntry, @@ -419,22 +606,26 @@ default void buildTo(Path path, } /** - * Build a module descriptor into a byte array. + * Builds a module descriptor into a byte array. + * * @param moduleAttribute the {@code Module} attribute - * @return the classfile bytes + * @return the {@code class} file bytes + * @throws IllegalArgumentException if building encounters a failure */ default byte[] buildModule(ModuleAttribute moduleAttribute) { return buildModule(moduleAttribute, clb -> {}); } /** - * Build a module descriptor into a byte array. + * Builds a module descriptor into a byte array. + * * @param moduleAttribute the {@code Module} attribute * @param handler a handler that receives a {@link ClassBuilder} - * @return the classfile bytes + * @return the {@code class} file bytes + * @throws IllegalArgumentException if building encounters a failure */ default byte[] buildModule(ModuleAttribute moduleAttribute, - Consumer handler) { + Consumer handler) { return build(CD_module_info, clb -> { clb.withFlags(AccessFlag.MODULE); clb.with(moduleAttribute); @@ -443,10 +634,12 @@ default byte[] buildModule(ModuleAttribute moduleAttribute, } /** - * Build a module descriptor into a file. + * Builds a module descriptor into a file in a file system. + * * @param path the file to write * @param moduleAttribute the {@code Module} attribute - * @throws java.io.IOException if an I/O error occurs + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if building encounters a failure */ default void buildModuleTo(Path path, ModuleAttribute moduleAttribute) throws IOException { @@ -454,11 +647,13 @@ default void buildModuleTo(Path path, } /** - * Build a module descriptor into a file. + * Builds a module descriptor into a file in a file system. + * * @param path the file to write * @param moduleAttribute the {@code Module} attribute * @param handler a handler that receives a {@link ClassBuilder} - * @throws java.io.IOException if an I/O error occurs + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if building encounters a failure */ default void buildModuleTo(Path path, ModuleAttribute moduleAttribute, @@ -467,251 +662,388 @@ default void buildModuleTo(Path path, } /** - * Transform one classfile into a new classfile with the aid of a - * {@link ClassTransform}. The transform will receive each element of + * Transform one {@code class} file into a new {@code class} file according + * to a {@link ClassTransform}. The transform will receive each element of * this class, as well as a {@link ClassBuilder} for building the new class. * The transform is free to preserve, remove, or replace elements as it * sees fit. - * - * @implNote + *

* This method behaves as if: * {@snippet lang=java : - * this.build(model.thisClass(), ConstantPoolBuilder.of(model), - * clb -> clb.transform(model, transform)); + * ConstantPoolBuilder cpb = null; // @replace substring=null; replacement=... + * this.build(model.thisClass(), cpb, + * clb -> clb.transform(model, transform)); * } + * where {@code cpb} is determined by {@link ConstantPoolSharingOption}. + * + * @apiNote + * This is named {@code transformClass} instead of {@code transform} for + * consistency with {@link ClassBuilder#transformField}, {@link + * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode}, + * and to distinguish from {@link ClassFileBuilder#transform}, which is + * more generic and powerful. * * @param model the class model to transform * @param transform the transform * @return the bytes of the new class + * @throws IllegalArgumentException if building encounters a failure + * @see ConstantPoolSharingOption */ default byte[] transformClass(ClassModel model, ClassTransform transform) { return transformClass(model, model.thisClass(), transform); } /** - * Transform one classfile into a new classfile with the aid of a - * {@link ClassTransform}. The transform will receive each element of + * Transform one {@code class} file into a new {@code class} file according + * to a {@link ClassTransform}. The transform will receive each element of * this class, as well as a {@link ClassBuilder} for building the new class. * The transform is free to preserve, remove, or replace elements as it * sees fit. * + * @apiNote + * This is named {@code transformClass} instead of {@code transform} for + * consistency with {@link ClassBuilder#transformField}, {@link + * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode}, + * and to distinguish from {@link ClassFileBuilder#transform}, which is + * more generic and powerful. + * * @param model the class model to transform * @param newClassName new class name * @param transform the transform * @return the bytes of the new class + * @throws IllegalArgumentException if building encounters a failure + * @see ConstantPoolSharingOption */ default byte[] transformClass(ClassModel model, ClassDesc newClassName, ClassTransform transform) { return transformClass(model, TemporaryConstantPool.INSTANCE.classEntry(newClassName), transform); } /** - * Transform one classfile into a new classfile with the aid of a - * {@link ClassTransform}. The transform will receive each element of + * Transform one {@code class} file into a new {@code class} file according + * to a {@link ClassTransform}. The transform will receive each element of * this class, as well as a {@link ClassBuilder} for building the new class. * The transform is free to preserve, remove, or replace elements as it * sees fit. - * - * @implNote + *

* This method behaves as if: * {@snippet lang=java : - * this.build(newClassName, ConstantPoolBuilder.of(model), - * clb -> clb.transform(model, transform)); + * ConstantPoolBuilder cpb = null; // @replace substring=null; replacement=... + * this.build(newClassName, cpb, clb -> clb.transform(model, transform)); * } + * where {@code cpb} is determined by {@link ConstantPoolSharingOption}. + * + * @apiNote + * This is named {@code transformClass} instead of {@code transform} for + * consistency with {@link ClassBuilder#transformField}, {@link + * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode}, + * and to distinguish from {@link ClassFileBuilder#transform}, which is + * more generic and powerful. * * @param model the class model to transform * @param newClassName new class name * @param transform the transform * @return the bytes of the new class + * @throws IllegalArgumentException if building encounters a failure + * @see ConstantPoolSharingOption */ byte[] transformClass(ClassModel model, ClassEntry newClassName, ClassTransform transform); /** - * Verify a classfile. Any verification errors found will be returned. + * Verify a {@code class} file. All verification errors found will be returned. + * * @param model the class model to verify - * @return a list of verification errors, or an empty list if no errors are + * @return a list of verification errors, or an empty list if no error is * found */ List verify(ClassModel model); /** - * Verify a classfile. Any verification errors found will be returned. - * @param bytes the classfile bytes to verify - * @return a list of verification errors, or an empty list if no errors are + * Verify a {@code class} file. All verification errors found will be returned. + * + * @param bytes the {@code class} file bytes to verify + * @return a list of verification errors, or an empty list if no error is * found */ List verify(byte[] bytes); /** - * Verify a classfile. Any verification errors found will be returned. - * @param path the classfile path to verify - * @return a list of verification errors, or an empty list if no errors are + * Verify a {@code class} file. All verification errors found will be returned. + * + * @param path the {@code class} file path to verify + * @return a list of verification errors, or an empty list if no error is * found - * @throws java.io.IOException if an I/O error occurs + * @throws IOException if an I/O error occurs */ default List verify(Path path) throws IOException { return verify(Files.readAllBytes(path)); } - /** 0xCAFEBABE */ + /** + * The magic number identifying the {@code class} file format, {@value + * "0x%04x" #MAGIC_NUMBER}. It is a big-endian 4-byte value. + */ int MAGIC_NUMBER = 0xCAFEBABE; - /** The bit mask of PUBLIC access and property modifier. */ + /** The bit mask of {@link AccessFlag#PUBLIC} access and property modifier. */ int ACC_PUBLIC = 0x0001; - /** The bit mask of PROTECTED access and property modifier. */ + /** The bit mask of {@link AccessFlag#PROTECTED} access and property modifier. */ int ACC_PROTECTED = 0x0004; - /** The bit mask of PRIVATE access and property modifier. */ + /** The bit mask of {@link AccessFlag#PRIVATE} access and property modifier. */ int ACC_PRIVATE = 0x0002; - /** The bit mask of INTERFACE access and property modifier. */ + /** The bit mask of {@link AccessFlag#INTERFACE} access and property modifier. */ int ACC_INTERFACE = 0x0200; - /** The bit mask of ENUM access and property modifier. */ + /** The bit mask of {@link AccessFlag#ENUM} access and property modifier. */ int ACC_ENUM = 0x4000; - /** The bit mask of ANNOTATION access and property modifier. */ + /** The bit mask of {@link AccessFlag#ANNOTATION} access and property modifier. */ int ACC_ANNOTATION = 0x2000; - /** The bit mask of SUPER access and property modifier. */ + /** The bit mask of {@link AccessFlag#SUPER} access and property modifier. */ int ACC_SUPER = 0x0020; - /** The bit mask of ABSTRACT access and property modifier. */ + /** The bit mask of {@link AccessFlag#ABSTRACT} access and property modifier. */ int ACC_ABSTRACT = 0x0400; - /** The bit mask of VOLATILE access and property modifier. */ + /** The bit mask of {@link AccessFlag#VOLATILE} access and property modifier. */ int ACC_VOLATILE = 0x0040; - /** The bit mask of TRANSIENT access and property modifier. */ + /** The bit mask of {@link AccessFlag#TRANSIENT} access and property modifier. */ int ACC_TRANSIENT = 0x0080; - /** The bit mask of SYNTHETIC access and property modifier. */ + /** The bit mask of {@link AccessFlag#SYNTHETIC} access and property modifier. */ int ACC_SYNTHETIC = 0x1000; - /** The bit mask of STATIC access and property modifier. */ + /** The bit mask of {@link AccessFlag#STATIC} access and property modifier. */ int ACC_STATIC = 0x0008; - /** The bit mask of FINAL access and property modifier. */ + /** The bit mask of {@link AccessFlag#FINAL} access and property modifier. */ int ACC_FINAL = 0x0010; - /** The bit mask of SYNCHRONIZED access and property modifier. */ + /** The bit mask of {@link AccessFlag#SYNCHRONIZED} access and property modifier. */ int ACC_SYNCHRONIZED = 0x0020; - /** The bit mask of BRIDGE access and property modifier. */ + /** The bit mask of {@link AccessFlag#BRIDGE} access and property modifier. */ int ACC_BRIDGE = 0x0040; - /** The bit mask of VARARGS access and property modifier. */ + /** The bit mask of {@link AccessFlag#VARARGS} access and property modifier. */ int ACC_VARARGS = 0x0080; - /** The bit mask of NATIVE access and property modifier. */ + /** The bit mask of {@link AccessFlag#NATIVE} access and property modifier. */ int ACC_NATIVE = 0x0100; - /** The bit mask of STRICT access and property modifier. */ + /** The bit mask of {@link AccessFlag#STRICT} access and property modifier. */ int ACC_STRICT = 0x0800; - /** The bit mask of MODULE access and property modifier. */ + /** The bit mask of {@link AccessFlag#MODULE} access and property modifier. */ int ACC_MODULE = 0x8000; - /** The bit mask of OPEN access and property modifier. */ + /** The bit mask of {@link AccessFlag#OPEN} access and property modifier. */ int ACC_OPEN = 0x20; - /** The bit mask of MANDATED access and property modifier. */ + /** The bit mask of {@link AccessFlag#MANDATED} access and property modifier. */ int ACC_MANDATED = 0x8000; - /** The bit mask of TRANSITIVE access and property modifier. */ + /** The bit mask of {@link AccessFlag#TRANSITIVE} access and property modifier. */ int ACC_TRANSITIVE = 0x20; - /** The bit mask of STATIC_PHASE access and property modifier. */ + /** The bit mask of {@link AccessFlag#STATIC_PHASE} access and property modifier. */ int ACC_STATIC_PHASE = 0x40; - /** The class major version of JAVA_1. */ + /** + * The class major version of the initial version of Java, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_0 + * @see ClassFileFormatVersion#RELEASE_1 + */ int JAVA_1_VERSION = 45; - /** The class major version of JAVA_2. */ + /** + * The class major version introduced by Java 2 SE 1.2, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_2 + */ int JAVA_2_VERSION = 46; - /** The class major version of JAVA_3. */ + /** + * The class major version introduced by Java 2 SE 1.3, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_3 + */ int JAVA_3_VERSION = 47; - /** The class major version of JAVA_4. */ + /** + * The class major version introduced by Java 2 SE 1.4, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_4 + */ int JAVA_4_VERSION = 48; - /** The class major version of JAVA_5. */ + /** + * The class major version introduced by Java 2 SE 5.0, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_5 + */ int JAVA_5_VERSION = 49; - /** The class major version of JAVA_6. */ + /** + * The class major version introduced by Java SE 6, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_6 + */ int JAVA_6_VERSION = 50; - /** The class major version of JAVA_7. */ + /** + * The class major version introduced by Java SE 7, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_7 + */ int JAVA_7_VERSION = 51; - /** The class major version of JAVA_8. */ + /** + * The class major version introduced by Java SE 8, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_8 + */ int JAVA_8_VERSION = 52; - /** The class major version of JAVA_9. */ + /** + * The class major version introduced by Java SE 9, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_9 + */ int JAVA_9_VERSION = 53; - /** The class major version of JAVA_10. */ + /** + * The class major version introduced by Java SE 10, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_10 + */ int JAVA_10_VERSION = 54; - /** The class major version of JAVA_11. */ + /** + * The class major version introduced by Java SE 11, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_11 + */ int JAVA_11_VERSION = 55; - /** The class major version of JAVA_12. */ + /** + * The class major version introduced by Java SE 12, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_12 + */ int JAVA_12_VERSION = 56; - /** The class major version of JAVA_13. */ + /** + * The class major version introduced by Java SE 13, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_13 + */ int JAVA_13_VERSION = 57; - /** The class major version of JAVA_14. */ + /** + * The class major version introduced by Java SE 14, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_14 + */ int JAVA_14_VERSION = 58; - /** The class major version of JAVA_15. */ + /** + * The class major version introduced by Java SE 15, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_15 + */ int JAVA_15_VERSION = 59; - /** The class major version of JAVA_16. */ + /** + * The class major version introduced by Java SE 16, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_16 + */ int JAVA_16_VERSION = 60; - /** The class major version of JAVA_17. */ + /** + * The class major version introduced by Java SE 17, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_17 + */ int JAVA_17_VERSION = 61; - /** The class major version of JAVA_18. */ + /** + * The class major version introduced by Java SE 18, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_18 + */ int JAVA_18_VERSION = 62; - /** The class major version of JAVA_19. */ + /** + * The class major version introduced by Java SE 19, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_19 + */ int JAVA_19_VERSION = 63; - /** The class major version of JAVA_20. */ + /** + * The class major version introduced by Java SE 20, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_20 + */ int JAVA_20_VERSION = 64; - /** The class major version of JAVA_21. */ + /** + * The class major version introduced by Java SE 21, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_21 + */ int JAVA_21_VERSION = 65; - /** The class major version of JAVA_22. */ + /** + * The class major version introduced by Java SE 22, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_22 + */ int JAVA_22_VERSION = 66; - /** The class major version of JAVA_23. */ + /** + * The class major version introduced by Java SE 23, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_23 + */ int JAVA_23_VERSION = 67; - /** The class major version of JAVA_24. */ + /** + * The class major version introduced by Java SE 24, {@value}. + * + * @see ClassFileFormatVersion#RELEASE_24 + */ int JAVA_24_VERSION = 68; /** - * A minor version number indicating a class uses preview features - * of a Java SE version since 12, for major versions {@value + * A minor version number {@value} indicating a class uses preview features + * of a Java SE release since 12, for major versions {@value * #JAVA_12_VERSION} and above. */ int PREVIEW_MINOR_VERSION = 65535; /** - * {@return the latest major Java version} + * {@return the latest class major version supported by the current runtime} */ static int latestMajorVersion() { return JAVA_24_VERSION; } /** - * {@return the latest minor Java version} + * {@return the latest class minor version supported by the current runtime} + * + * @apiNote + * This does not report the {@link #PREVIEW_MINOR_VERSION} when the current + * runtime has preview feature enabled, as {@code class} files with a major + * version other than {@link #latestMajorVersion()} and the preview minor + * version are not supported. */ static int latestMinorVersion() { return 0; diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java index 01eeefd0b9b1a..172daa83cf862 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.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 @@ -31,17 +31,36 @@ import jdk.internal.classfile.impl.TransformImpl; /** - * A builder for a classfile or portion of a classfile. Builders are rarely - * created directly; they are passed to handlers by methods such as - * {@link ClassFile#build(ClassDesc, Consumer)} or to transforms. - * Elements of the newly built entity can be specified - * abstractly (by passing a {@link ClassFileElement} to {@link #with(ClassFileElement)} - * or concretely by calling the various {@code withXxx} methods. + * A builder for a {@link CompoundElement}, which accepts the member elements + * to be integrated into the built structure. Builders are usually passed as + * an argument to {@link Consumer} handlers, such as in {@link + * ClassFile#build(ClassDesc, Consumer)}. The handlers should deliver elements + * to a builder similar to how a {@link CompoundElement} traverses its member + * elements. + *

+ * The basic way a builder accepts elements is through {@link #with}, which + * supports call chaining. Concrete subtypes of builders usually define extra + * methods to define elements directly to the builder, such as {@link + * ClassBuilder#withFlags(int)} or {@link CodeBuilder#aload(int)}. + *

+ * Whether a member element can appear multiple times in a compound structure + * affects the behavior of the element in {@code ClassFileBuilder}s. If an + * element can appear at most once but multiple instances are supplied to a + * {@code ClassFileBuilder}, the last supplied instance appears on the built + * structure. If an element appears exactly once but no instance is supplied, + * an unspecified default value element may be used in that structure. + *

+ * Due to restrictions of the {@code class} file format, certain member elements + * that can be modeled by the API cannot be represented in the built structure + * under specific circumstances. Passing such elements to the builder causes + * {@link IllegalArgumentException}. Some {@link ClassFile.Option}s control + * whether such elements should be altered or dropped to produce valid {@code + * class} files. * - * @param the element type - * @param the builder type + * @param the member element type + * @param the self type of this builder + * @see CompoundElement * @see ClassFileTransform - * * @sealedGraph * @since 24 */ @@ -49,8 +68,15 @@ public sealed interface ClassFileBuilder permits ClassBuilder, FieldBuilder, MethodBuilder, CodeBuilder { /** - * Integrate the {@link ClassFileElement} into the entity being built. - * @param e the element + * Integrates the member element into the structure being built. + * + * @apiNote + * This method exists to implement {@link Consumer}; users can use {@link + * #with} for call chaining. + * + * @param e the member element + * @throws IllegalArgumentException if the member element cannot be + * represented in the {@code class} file format */ @Override default void accept(E e) { @@ -58,9 +84,12 @@ default void accept(E e) { } /** - * Integrate the {@link ClassFileElement} into the entity being built. - * @param e the element + * Integrates the member element into the structure being built. + * + * @param e the member element * @return this builder + * @throws IllegalArgumentException if the member element cannot be + * represented in the {@code class} file format */ B with(E e); @@ -70,10 +99,29 @@ default void accept(E e) { ConstantPoolBuilder constantPool(); /** - * Apply a transform to a model, directing results to this builder. - * @param model the model to transform + * Applies a transform to a compound structure, directing results to this + * builder. + *

+ * The transform will receive each element of the compound structure, as + * well as this builder for building the structure. The transform is free + * to preserve, remove, or replace elements as it sees fit. + *

+ * A builder can run multiple transforms against different compound + * structures, integrating member elements of different origins. + * + * @apiNote + * Many subinterfaces have methods like {@link ClassBuilder#transformMethod} + * or {@link MethodBuilder#transformCode}. However, calling them is + * fundamentally different from calling this method: those methods call the + * {@code transform} on the child builders instead of on itself. For + * example, {@code classBuilder.transformMethod} calls {@code + * methodBuilder.transform} with a new method builder instead of calling + * {@code classBuilder.transform} on itself. + * + * @param model the structure to transform * @param transform the transform to apply * @return this builder + * @see ClassFileTransform */ default B transform(CompoundElement model, ClassFileTransform transform) { @SuppressWarnings("unchecked") diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java index ed84eb39d53b8..5d5d00209498c 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.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 @@ -25,12 +25,34 @@ package java.lang.classfile; /** - * Immutable model for a portion of (or the entirety of) a classfile. Elements - * that model parts of the classfile that have attributes will implement {@link - * AttributedElement}; elements that model complex parts of the classfile that - * themselves contain their own child elements will implement {@link - * CompoundElement}. Elements specific to various locations in the classfile - * will implement {@link ClassElement}, {@link MethodElement}, etc. + * Marker interface for structures with special capabilities in the {@code + * class} file format. {@link AttributedElement} indicates a structure has + * {@link Attribute}s. {@link CompoundElement} indicates a structure can be + * viewed as a composition of member structures, whose memberships are marked by + * {@link ClassElement}, {@link MethodElement}, {@link FieldElement}, or {@link + * CodeElement}. + * + *

Membership Elements

+ * {@link ClassModel}, {@link MethodModel}, {@link FieldModel}, and {@link + * CodeModel} each has a dedicated interface marking its member structures: + * {@link ClassElement}, {@link MethodElement}, {@link FieldElement}, and + * {@link CodeElement}. They can be supplied to a {@link ClassBuilder}, a + * {@link MethodBuilder}, a {@link FieldBuilder}, or a {@link CodeBuilder} to be + * included as members of the built model. Unless otherwise specified, these + * structures are delivered during the {@linkplain CompoundElement traversal} of + * the corresponding models. Some of these elements may appear at most once or + * exactly once in the traversal of the models; such elements have special + * treatment by {@link ClassFileBuilder} and are specified in their modeling + * interfaces. If such elements appear multiple times during traversal, the + * last occurrence should be used and all previous instances should be + * discarded. + *

+ * These membership element marker interfaces are sealed; future versions of the + * Java SE Platform may define new elements to the sealed hierarchy when the + * {@code class} file format for the Java Platform evolves. Using an exhaustive + * pattern matching switch over these hierarchies indicates the user only wish + * the processing code to run on a specific version of Java Platform, and will + * fail if unknown new elements are encountered. * * @sealedGraph * @since 24 diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java index 7d9385eed6898..9126b13bce942 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.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 @@ -25,44 +25,74 @@ package java.lang.classfile; import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; +import java.util.function.Predicate; import java.util.function.Supplier; /** - * A transformation on streams of elements. Transforms are used during - * transformation of classfile entities; a transform is provided to a method like - * {@link ClassFile#transformClass(ClassModel, ClassTransform)}, and the elements of the class, - * along with a builder, are presented to the transform. - * - *

The subtypes of {@linkplain - * ClassFileTransform} (e.g., {@link ClassTransform}) are functional interfaces - * that accept an element and a corresponding builder. Since any element can be - * reproduced on the builder via {@link ClassBuilder#with(ClassFileElement)}, a - * transform can easily leave elements in place, remove them, replace them, or - * augment them with other elements. This enables localized transforms to be - * represented concisely. - * - *

Transforms also have an {@link #atEnd(ClassFileBuilder)} method, for - * which the default implementation does nothing, so that a transform can - * perform additional building after the stream of elements is exhausted. - * - *

Transforms can be chained together via the {@link - * #andThen(ClassFileTransform)} method, so that the output of one becomes the - * input to another. This allows smaller units of transformation to be captured - * and reused. - * - *

Some transforms are stateful; for example, a transform that injects an - * annotation on a class may watch for the {@link RuntimeVisibleAnnotationsAttribute} - * element and transform it if found, but if it is not found, will generate a - * {@linkplain RuntimeVisibleAnnotationsAttribute} element containing the - * injected annotation from the {@linkplain #atEnd(ClassFileBuilder)} handler. - * To do this, the transform must accumulate some state during the traversal so - * that the end handler knows what to do. If such a transform is to be reused, - * its state must be reset for each traversal; this will happen automatically if - * the transform is created with {@link ClassTransform#ofStateful(Supplier)} (or - * corresponding methods for other classfile locations.) + * A transformation on a {@link CompoundElement} by processing its individual + * member elements and sending the results to a {@link ClassFileBuilder}, + * through {@link ClassFileBuilder#transform}. A subtype of {@code + * ClassFileTransform} is defined for each subtype of {@link CompoundElement} + * and {@link ClassFileBuilder}, as shown in the sealed class hierarchy below. + *

+ * For example, this is a basic transformation of a {@link CodeModel} that + * redirects all calls to static methods in the {@code Foo} class to the {@code + * Bar} class, preserving all other elements: + * {@snippet file="PackageSnippets.java" region=fooToBarTransform} + * Note that if no transformation of a member element is desired, the element + * should be presented to {@link ClassFileBuilder#with builder::with}. If no + * action is taken, that member element is dropped. + *

+ * More advanced usages of transforms include {@linkplain ##start-end start or + * end handling}, {@linkplain ##stateful stateful transformation} that makes a + * decision based on previously encountered member elements, and {@linkplain + * ##composition composition} of transforms, where one transform processes the + * results of a previous transform on the input compound structure. All these + * capabilities are supported by this interface and accessible to user transform + * implementations. + *

+ * Users can define custom start and end handling for a transform by overriding + * {@link #atStart} and {@link #atEnd}. The start handler is called before any + * member element is processed, and the end handler is called after all member + * elements are processed. For example, the start handler can be used to inject + * extra code elements to the beginning of a code array, and the end handler, + * combined with stateful transformation, can perform cleanup actions, such as + * determining if an attribute has been merged, or if a new attribute should be + * defined. Each subtype of {@code ClassFileTransform} defines a utility method + * {@code endHandler} that returns a transform that only has end handling. + *

+ * Transforms can have states that persist across processing of individual + * member elements. For example, if a transform injects an annotation, the + * transform may keep track if it has encountered and presented an updated + * {@link RuntimeVisibleAnnotationsAttribute} to the builder; if it has not yet, + * it can present a new attribute containing only the injected annotation in its + * end handler. If such a transform is to be shared or reused, each returned + * transform should have its own state. Each subtype of {@code ClassFileTransform} + * defines a utility method {@code ofStateful} where a supplier creates the + * transform at its initial state each time the transform is reused. + *

+ * Transforms can be composed via {@link #andThen}. When this transform is + * composed with another transform, it means the output member elements received + * by the {@link ClassFileBuilder} become the input elements to that other + * transform. Composition avoids building intermediate structures for multiple + * transforms to run on. Each subtype of {@code ClassFileTransform} implements + * {@link #andThen}, which generally should not be implemented by users. + *

+ * Transforms that run on smaller structures can be lifted to its enclosing + * structures to selectively run on all enclosed smaller structures of the same + * kind. For example, a {@link CodeTransform} can be lifted via {@link + * ClassTransform#transformingMethodBodies(Predicate, CodeTransform)} to + * transform the method body of select methods in the class it runs on. This + * allows users to write small transforms and apply to larger scales. + *

+ * Besides {@link ClassFileBuilder#transform}, there are other methods that + * accepts a transform conveniently, such as {@link ClassFile#transformClass}, + * {@link ClassBuilder#transformField}, {@link ClassBuilder#transformMethod}, or + * {@link MethodBuilder#transformCode}. They are convenience methods that suit + * the majority of transformation scenarios. * * @param the transform type - * @param the element type + * @param the member element type * @param the builder type * * @sealedGraph @@ -79,6 +109,9 @@ public sealed interface ClassFileTransform< * body.) If no transformation is desired, the element can be presented to * {@link B#with(ClassFileElement)}. If the element is to be dropped, no * action is required. + *

+ * This method is called by the Class-File API. Users should never call + * this method. * * @param builder the builder for the new entity * @param element the element @@ -89,6 +122,9 @@ public sealed interface ClassFileTransform< * Take any final action during transformation of a classfile entity. Called * after all elements of the class are presented to {@link * #accept(ClassFileBuilder, ClassFileElement)}. + *

+ * This method is called by the Class-File API. Users should never call + * this method. * * @param builder the builder for the new entity * @implSpec The default implementation does nothing. @@ -100,6 +136,9 @@ default void atEnd(B builder) { * Take any preliminary action during transformation of a classfile entity. * Called before any elements of the class are presented to {@link * #accept(ClassFileBuilder, ClassFileElement)}. + *

+ * This method is called by the Class-File API. Users should never call + * this method. * * @param builder the builder for the new entity * @implSpec The default implementation does nothing. @@ -110,6 +149,10 @@ default void atStart(B builder) { /** * Chain this transform with another; elements presented to the builder of * this transform will become the input to the next transform. + *

+ * This method is implemented by the Class-File API. Users usually don't + * have sufficient access to Class-File API functionalities to override this + * method correctly for generic downstream transforms. * * @param next the downstream transform * @return the chained transform diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java b/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java index 1916a185cc863..5a795b9486533 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.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 @@ -24,32 +24,62 @@ */ package java.lang.classfile; +import java.lang.reflect.ClassFileFormatVersion; + import jdk.internal.classfile.impl.ClassFileVersionImpl; /** - * Models the classfile version information for a class. Delivered as a {@link - * java.lang.classfile.ClassElement} when traversing the elements of a {@link - * ClassModel}. + * Models the minor and major version numbers of a {@code class} file (JVMS + * {@jvms 4.1}). The {@code class} file version appears exactly once in each + * class, and is set to an unspecified default value if not explicitly provided. + *

+ * The major versions of {@code class} file format begins at {@value + * ClassFile#JAVA_1_VERSION} for Java Platform version 1.0.2, and is continuous + * up to {@link ClassFile#latestMajorVersion()}. In general, each major version + * defines a new supported {@code class} file format, modeled by {@link + * ClassFileFormatVersion}, and supports all previous formats. + *

+ * For major versions up to {@value ClassFile#JAVA_11_VERSION} for Java SE + * Platform 11, the minor version of any value is supported. For major versions + * {@value ClassFile#JAVA_12_VERSION} for Java SE Platform version 12 and above, + * the minor version must be {@code 0} or {@value ClassFile#PREVIEW_MINOR_VERSION}. + * The minor version {@code 0} is always supported, and represents the format + * modeled by {@link ClassFileFormatVersion}. The minor version {@code 65535} + * indicates the {@code class} file uses preview features of the Java SE + * Platform release represented by the major version. A Java Virtual Machine + * can only load such a {@code class} file if it has the same Java SE Platform + * version and the JVM has preview features enabled. * + * @see ClassModel#majorVersion() + * @see ClassModel#minorVersion() + * @see ClassFileFormatVersion + * @jvms 4.1 The {@code ClassFile} Structure * @since 24 */ public sealed interface ClassFileVersion extends ClassElement permits ClassFileVersionImpl { /** - * {@return the major classfile version} + * {@return the major version} It is in the range of unsigned short, {@code + * [0, 65535]}. + * + * @apiNote + * Constants in {@link ClassFile} named {@code Java_#_VERSION}, where # is + * a release number, such as {@link ClassFile#JAVA_21_VERSION}, describe the + * class major versions of the Java Platform SE. */ int majorVersion(); /** - * {@return the minor classfile version} + * {@return the minor version} It is in the range of unsigned short, {@code + * [0, 65535]}. */ int minorVersion(); /** * {@return a {@link ClassFileVersion} element} - * @param majorVersion the major classfile version - * @param minorVersion the minor classfile version + * @param majorVersion the major version + * @param minorVersion the minor version */ static ClassFileVersion of(int majorVersion, int minorVersion) { return new ClassFileVersionImpl(majorVersion, minorVersion); diff --git a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java index 2c612854a64d2..3bc56b43b7b1b 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.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 @@ -24,7 +24,11 @@ */ package java.lang.classfile; +import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.classfile.ClassFile.StackMapsOption; +import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.constant.ClassDesc; import java.lang.invoke.MethodHandles; import java.util.Collection; @@ -42,27 +46,42 @@ import static java.util.Objects.requireNonNull; /** - * Provides class hierarchy information for generating correct stack maps - * during code building. + * Provides class hierarchy information for {@linkplain StackMapsOption stack + * maps generation} and {@linkplain ClassFile#verify(byte[]) verification}. + * A class hierarchy resolver must be able to process all classes and interfaces + * encountered during these workloads. * + * @see ClassFile.ClassHierarchyResolverOption + * @see StackMapTableAttribute + * @jvms 4.10.1.2 Verification Type System * @since 24 */ @FunctionalInterface public interface ClassHierarchyResolver { /** - * {@return the default instance of {@linkplain ClassHierarchyResolver} that + * {@return the default instance of {@code ClassHierarchyResolver} that * gets {@link ClassHierarchyInfo} from system class loader with reflection} + * This default instance cannot load classes from other class loaders, such + * as the caller's class loader; it also loads the system classes if they + * are not yet loaded, which makes it unsuitable for instrumentation. */ static ClassHierarchyResolver defaultResolver() { return ClassHierarchyImpl.DEFAULT_RESOLVER; } /** - * {@return the {@link ClassHierarchyInfo} for a given class name, or null - * if the name is unknown to the resolver} + * {@return the {@code ClassHierarchyInfo} for a given class name, or {@code + * null} if the name is unknown to the resolver} + *

+ * This method is called by the Class-File API to obtain the hierarchy + * information of a class or interface; users should not call this method. + * The symbolic descriptor passed by the Class-File API always represents + * a class or interface. + * * @param classDesc descriptor of the class - * @throws IllegalArgumentException if a class shouldn't be queried for hierarchy + * @throws IllegalArgumentException if a class shouldn't be queried for + * hierarchy, such as when it is inaccessible */ ClassHierarchyInfo getClassInfo(ClassDesc classDesc); @@ -78,6 +97,7 @@ sealed interface ClassHierarchyInfo permits ClassHierarchyImpl.ClassHierarchyInf * * @param superClass descriptor of the super class, may be {@code null} * @return the info indicating the super class + * @see Superclass */ static ClassHierarchyInfo ofClass(ClassDesc superClass) { return new ClassHierarchyImpl.ClassHierarchyInfoImpl(superClass, false); @@ -94,14 +114,15 @@ static ClassHierarchyInfo ofInterface() { } /** - * Chains this {@linkplain ClassHierarchyResolver} with another to be - * consulted if this resolver does not know about the specified class. + * Chains this {@code ClassHierarchyResolver} with another to be consulted + * if this resolver does not know about the specified class. + * + * @implSpec + * The default implementation returns resolver implemented to query {@code + * other} resolver in case this resolver returns {@code null}. * * @param other the other resolver * @return the chained resolver - * - * @implSpec The default implementation returns resolver implemented to ask - * other resolver in cases where this resolver returns {@code null}. */ default ClassHierarchyResolver orElse(ClassHierarchyResolver other) { requireNonNull(other); @@ -117,32 +138,32 @@ public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { } /** - * Returns a ClassHierarchyResolver that caches class hierarchy information from this - * resolver. The returned resolver will not update if delegate resolver returns differently. - * The thread safety of the returned resolver depends on the thread safety of the map + * {@return a {@code ClassHierarchyResolver} that caches class hierarchy + * information from this resolver} The returned resolver will not update if + * the query results from this resolver changed over time. The thread + * safety of the returned resolver depends on the thread safety of the map * returned by the {@code cacheFactory}. * - * @param cacheFactory the factory for the cache - * @return the ClassHierarchyResolver with caching + * @implSpec + * The default implementation returns a resolver holding an instance of the + * cache map provided by the {@code cacheFactory}. It looks up in the cache + * map, or if a class name has not yet been queried, queries this resolver + * and caches the result, including a {@code null} that indicates unknown + * class names. The cache map may refuse {@code null} keys and values. * - * @implSpec The default implementation returns resolver holding an instance - * of the cache map provided by the {@code cacheFactory}. It asks - * the cache map always first and fills the cache map with all - * resolved and also unresolved class info. The cache map may refuse - * {@code null} keys and values. + * @param cacheFactory the factory for the cache */ default ClassHierarchyResolver cached(Supplier> cacheFactory) { return new ClassHierarchyImpl.CachedClassHierarchyResolver(this, cacheFactory.get()); } /** - * Returns a ClassHierarchyResolver that caches class hierarchy information from this - * resolver. The returned resolver will not update if delegate resolver returns differently. - * The returned resolver is not thread-safe. + * {@return a {@code ClassHierarchyResolver} that caches class hierarchy + * information from this resolver} The returned resolver will not update if + * the query results from this resolver changed over time. The returned + * resolver is not thread-safe. * {@snippet file="PackageSnippets.java" region="lookup-class-hierarchy-resolver"} * - * @return the ClassHierarchyResolver - * * @implSpec The default implementation calls {@link #cached(Supplier)} with * {@link HashMap} supplier as {@code cacheFactory}. */ @@ -160,24 +181,24 @@ public Map get() { } /** - * Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy - * information from classfiles located by a mapping function. The mapping function - * should return null if it cannot provide a mapping for a classfile. Any IOException - * from the provided input stream is rethrown as an UncheckedIOException. + * {@return a {@code ClassHierarchyResolver} that extracts class hierarchy + * information from {@code class} files returned by a mapping function} The + * mapping function should return {@code null} if it cannot provide a + * {@code class} file for a class name. Any {@link IOException} from the + * provided input stream is rethrown as an {@link UncheckedIOException} + * in {@link #getClassInfo(ClassDesc)}. * - * @param classStreamResolver maps class descriptors to classfile input streams - * @return the {@linkplain ClassHierarchyResolver} + * @param classStreamResolver maps class descriptors to {@code class} file input streams */ static ClassHierarchyResolver ofResourceParsing(Function classStreamResolver) { return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(requireNonNull(classStreamResolver)); } /** - * Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy - * information from classfiles located by a class loader. + * {@return a {@code ClassHierarchyResolver} that extracts class hierarchy + * information from {@code class} files located by a class loader} * * @param loader the class loader, to find class files - * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver ofResourceParsing(ClassLoader loader) { requireNonNull(loader); @@ -190,24 +211,22 @@ public InputStream apply(ClassDesc classDesc) { } /** - * Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy - * information from collections of class hierarchy metadata + * {@return a {@code ClassHierarchyResolver} that extracts class hierarchy + * information from collections of class hierarchy metadata} * * @param interfaces a collection of classes known to be interfaces * @param classToSuperClass a map from classes to their super classes - * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver of(Collection interfaces, - Map classToSuperClass) { + Map classToSuperClass) { return new StaticClassHierarchyResolver(interfaces, classToSuperClass); } /** - * Returns a ClassHierarchyResolver that extracts class hierarchy information via - * the Reflection API with a {@linkplain ClassLoader}. + * {@return a {@code ClassHierarchyResolver} that extracts class hierarchy + * information via classes loaded by a class loader with reflection} * * @param loader the class loader - * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(ClassLoader loader) { requireNonNull(loader); @@ -224,13 +243,13 @@ public Class apply(ClassDesc cd) { } /** - * Returns a ClassHierarchyResolver that extracts class hierarchy information via - * the Reflection API with a {@linkplain MethodHandles.Lookup Lookup}. If the class - * resolved is inaccessible to the given lookup, it throws {@link + * {@return a {@code ClassHierarchyResolver} that extracts class hierarchy + * information via classes accessible to a {@link MethodHandles.Lookup} + * with reflection} If the class resolved is inaccessible to the given + * lookup, {@link #getClassInfo(ClassDesc)} throws {@link * IllegalArgumentException} instead of returning {@code null}. * * @param lookup the lookup, must be able to access classes to resolve - * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(MethodHandles.Lookup lookup) { requireNonNull(lookup); diff --git a/src/java.base/share/classes/java/lang/classfile/ClassModel.java b/src/java.base/share/classes/java/lang/classfile/ClassModel.java index db804348cfe9e..2c547e6336df0 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassModel.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassModel.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 @@ -25,18 +25,41 @@ package java.lang.classfile; +import java.lang.classfile.attribute.BootstrapMethodsAttribute; +import java.lang.classfile.attribute.ModuleAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPool; +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.constant.ClassDesc; +import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import jdk.internal.classfile.impl.ClassImpl; /** - * Models a classfile. The contents of the classfile can be traversed via - * a streaming view, or via random access (e.g., - * {@link #flags()}), or by freely mixing the two. + * Models a {@code class} file. A {@code class} file can be viewed as a + * {@linkplain CompoundElement composition} of {@link ClassElement}s, or by + * random access via accessor methods if only specific parts of the {@code + * class} file is needed. + *

+ * Use {@link ClassFile#parse(byte[])}, which parses the binary data of a {@code + * class} file into a model, to obtain a {@code ClassModel}. + *

+ * To construct a {@code class} file, use {@link ClassFile#build(ClassDesc, + * Consumer)}. {@link ClassFile#transformClass(ClassModel, ClassTransform)} + * allows creating a new class by selectively processing the original class + * elements and directing the results to a class builder. + *

+ * A class holds attributes, most of which are accessible as member elements. + * {@link BootstrapMethodsAttribute} can only be accessed via {@linkplain + * AttributedElement explicit attribute reading}, as it is modeled as part of + * the {@linkplain #constantPool() constant pool}. * + * @see ClassFile#parse(byte[]) + * @see ClassTransform + * @jvms 4.1 The {@code ClassFile} Structure * @since 24 */ public sealed interface ClassModel @@ -45,19 +68,35 @@ public sealed interface ClassModel /** * {@return the constant pool for this class} + * + * @see ConstantPoolBuilder#of(ClassModel) */ ConstantPool constantPool(); - /** {@return the access flags} */ + /** + * {@return the access flags} + * + * @see AccessFlag.Location#CLASS + */ AccessFlags flags(); /** {@return the constant pool entry describing the name of this class} */ ClassEntry thisClass(); - /** {@return the major classfile version} */ + /** + * {@return the major version of this class} It is in the range of unsigned + * short, {@code [0, 65535]}. + * + * @see ClassFileVersion + */ int majorVersion(); - /** {@return the minor classfile version} */ + /** + * {@return the minor version of this class} It is in the range of unsigned + * short, {@code [0, 65535]}. + * + * @see ClassFileVersion + */ int minorVersion(); /** {@return the fields of this class} */ @@ -66,12 +105,28 @@ public sealed interface ClassModel /** {@return the methods of this class} */ List methods(); - /** {@return the superclass of this class, if there is one} */ + /** + * {@return the superclass of this class, if there is one} + * This {@code class} file may have no superclass if this represents a + * {@linkplain #isModuleInfo() module descriptor} or the {@link Object} + * class; otherwise, it must have a superclass. If this is an interface, + * the superclass must be {@link Object}. + * + * @see Superclass + */ Optional superclass(); - /** {@return the interfaces implemented by this class} */ + /** + * {@return the interfaces implemented by this class} + * + * @see Interfaces + */ List interfaces(); - /** {@return whether this class is a module descriptor} */ + /** + * {@return whether this {@code class} file is a module descriptor} + * + * @see ClassFile#buildModule(ModuleAttribute, Consumer) + */ boolean isModuleInfo(); } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassTransform.java index 82b61a1508952..c365b79f463a2 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassTransform.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,9 +35,12 @@ /** * A transformation on streams of {@link ClassElement}. + *

+ * Refer to {@link ClassFileTransform} for general guidance and caution around + * the use of transforms for structures in the {@code class} file format. * - * @see ClassFileTransform - * + * @see ClassModel + * @see ClassFile#transformClass(ClassModel, ClassTransform) * @since 24 */ @FunctionalInterface @@ -45,7 +48,7 @@ public non-sealed interface ClassTransform extends ClassFileTransform { /** - * A class transform that sends all elements to the builder. + * A class transform that passes all elements to the builder. */ static final ClassTransform ACCEPT_ALL = new ClassTransform() { @Override @@ -55,7 +58,7 @@ public void accept(ClassBuilder builder, ClassElement element) { }; /** - * Create a stateful class transform from a {@link Supplier}. The supplier + * Creates a stateful class transform from a {@link Supplier}. The supplier * will be invoked for each transformation. * * @param supplier a {@link Supplier} that produces a fresh transform object @@ -67,7 +70,7 @@ static ClassTransform ofStateful(Supplier supplier) { } /** - * Create a class transform that passes each element through to the builder, + * Creates a class transform that passes each element through to the builder, * and calls the specified function when transformation is complete. * * @param finisher the function to call when transformation is complete @@ -89,8 +92,8 @@ public void atEnd(ClassBuilder builder) { } /** - * Create a class transform that passes each element through to the builder, - * except for those that the supplied {@link Predicate} is true for. + * Creates a class transform that passes each element through to the builder, + * except for those that the supplied {@link Predicate} returns true for. * * @param filter the predicate that determines which elements to drop * @return the class transform @@ -104,8 +107,10 @@ static ClassTransform dropping(Predicate filter) { } /** - * Create a class transform that transforms {@link MethodModel} elements - * with the supplied method transform. + * Creates a class transform that transforms {@link MethodModel} elements + * with the supplied method transform for methods that the supplied {@link + * Predicate} returns true for, passing other elements through to the + * builder. * * @param filter a predicate that determines which methods to transform * @param xform the method transform @@ -117,8 +122,9 @@ static ClassTransform transformingMethods(Predicate filter, } /** - * Create a class transform that transforms {@link MethodModel} elements - * with the supplied method transform. + * Creates a class transform that transforms {@link MethodModel} elements + * with the supplied method transform, passing other elements through to the + * builder. * * @param xform the method transform * @return the class transform @@ -128,8 +134,10 @@ static ClassTransform transformingMethods(MethodTransform xform) { } /** - * Create a class transform that transforms the {@link CodeAttribute} (method body) - * of {@link MethodModel} elements with the supplied code transform. + * Creates a class transform that transforms the {@link CodeAttribute} (method body) + * of {@link MethodModel} elements with the supplied code transform for + * methods that the supplied {@link Predicate} returns true for, passing + * other elements through to the builder. * * @param filter a predicate that determines which methods to transform * @param xform the code transform @@ -141,8 +149,9 @@ static ClassTransform transformingMethodBodies(Predicate filter, } /** - * Create a class transform that transforms the {@link CodeAttribute} (method body) - * of {@link MethodModel} elements with the supplied code transform. + * Creates a class transform that transforms the {@link CodeAttribute} (method body) + * of {@link MethodModel} elements with the supplied code transform, passing + * other elements through to the builder. * * @param xform the code transform * @return the class transform @@ -152,8 +161,9 @@ static ClassTransform transformingMethodBodies(CodeTransform xform) { } /** - * Create a class transform that transforms {@link FieldModel} elements - * with the supplied field transform. + * Creates a class transform that transforms {@link FieldModel} elements + * with the supplied field transform, passing other elements through to the + * builder. * * @param xform the field transform * @return the class transform 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 3cbcb893cb11d..9eafaa2df9c3a 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.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 @@ -25,6 +25,7 @@ package java.lang.classfile; +import java.lang.classfile.ClassFile.*; import java.lang.classfile.constantpool.*; import java.lang.classfile.instruction.*; import java.lang.constant.ClassDesc; @@ -44,12 +45,23 @@ import static jdk.internal.classfile.impl.BytecodeHelpers.handleDescToHandleInfo; /** - * A builder for code attributes (method bodies). Builders are not created - * directly; they are passed to handlers by methods such as {@link - * MethodBuilder#withCode(Consumer)} or to code transforms. The elements of a - * code can be specified abstractly, by passing a {@link CodeElement} to {@link - * #with(ClassFileElement)} or concretely by calling the various {@code withXxx} - * methods. + * A builder for {@link CodeModel Code} attributes (method bodies). {@link + * MethodBuilder#withCode} is the basic way to obtain a code builder; {@link + * ClassBuilder#withMethodBody} is a shortcut. There are also derived code + * builders from {@link #block}, which handles code blocks and {@link + * #transforming}, which runs transforms on existing handlers, both of which + * requires a code builder to be available first. + *

+ * Refer to {@link ClassFileBuilder} for general guidance and caution around + * the use of builders for structures in the {@code class} file format. Unlike + * in other builders, the order of member elements in a code builder is + * significant: they affect the resulting bytecode. Many Class-File API options + * affect code builders: {@link DeadCodeOption} and {@link ShortJumpsOption} + * affect the resulting bytecode, and {@link DeadLabelsOption}, {@link + * DebugElementsOption}, {@link LineNumbersOption}, {@link StackMapsOption}, and + * {@link AttributesProcessingOption} affect the resulting attributes on the + * built {@code Code} attribute, that some elements sent to a code builder is + * otherwise ignored. * *

Instruction Factories

* {@code CodeBuilder} provides convenience methods to create instructions (See @@ -60,52 +72,63 @@ * #aload aload}. Note that some constant instructions, such as {@link #iconst_1 * iconst_1}, do not have generic versions, and thus have their own factories. *
  • Instructions that accept wide operands, such as {@code ldc2_w} or {@code - * wide}, share their factories with their regular version like {@link #ldc}. Note - * that {@link #goto_w goto_w} has its own factory to avoid {@linkplain - * ClassFile.ShortJumpsOption short jumps}. + * wide}, share their factories with their regular version like {@link #ldc}. + * Note that {@link #goto_w goto_w} has its own factory to avoid {@linkplain + * ShortJumpsOption short jumps}. *
  • The {@code goto}, {@code instanceof}, {@code new}, and {@code return} * instructions' factories are named {@link #goto_ goto_}, {@link #instanceOf * instanceOf}, {@link #new_ new_}, and {@link #return_() return_} respectively, * due to clashes with keywords in the Java programming language. - *
  • Factories are not provided for instructions {@code jsr}, {@code jsr_w}, - * {@code ret}, and {@code wide ret}, which cannot appear in class files with - * major version {@value ClassFile#JAVA_7_VERSION} or higher. (JVMS {@jvms 4.9.1}) + *
  • Factories are not provided for instructions {@link Opcode#JSR jsr}, + * {@link Opcode#JSR_W jsr_w}, {@link Opcode#RET ret}, and {@link Opcode#RET_W + * wide ret}, which cannot appear in class files with major version {@value + * ClassFile#JAVA_7_VERSION} or higher. (JVMS {@jvms 4.9.1}) They can still be + * provided via {@link #with}. * * + * @see MethodBuilder#withCode + * @see CodeModel * @see CodeTransform - * * @since 24 */ public sealed interface CodeBuilder extends ClassFileBuilder permits CodeBuilder.BlockCodeBuilder, ChainedCodeBuilder, TerminalCodeBuilder, NonterminalCodeBuilder { - /** {@return a fresh unbound label} */ + /** + * {@return a fresh unbound label} + * The label can be bound with {@link #labelBinding}. + */ Label newLabel(); - /** {@return the label associated with the beginning of the current block} - * If the current {@linkplain CodeBuilder} is not a "block" builder, such as - * those provided by {@link #block(Consumer)} or {@link #ifThenElse(Consumer, Consumer)}, - * the current block will be the entire method body. */ + /** + * {@return the label associated with the beginning of the current block} + * If this builder is not a "block" builder, such as those provided by + * {@link #block(Consumer)} or {@link #ifThenElse(Consumer, Consumer)}, + * the current block will be the entire method body. + */ Label startLabel(); - /** {@return the label associated with the end of the current block} - * If the current {@linkplain CodeBuilder} is not a "block" builder, such as - * those provided by {@link #block(Consumer)} or {@link #ifThenElse(Consumer, Consumer)}, - * the current block will be the entire method body. */ + /** + * {@return the label associated with the end of the current block} + * If this builder is not a "block" builder, such as those provided by + * {@link #block(Consumer)} or {@link #ifThenElse(Consumer, Consumer)}, + * the current block will be the entire method body. + */ Label endLabel(); /** - * {@return the local variable slot associated with the receiver}. + * {@return the local variable slot associated with the receiver} * * @throws IllegalStateException if this is a static method */ int receiverSlot(); /** - * {@return the local variable slot associated with the specified parameter}. + * {@return the local variable slot associated with the specified parameter} * The returned value is adjusted for the receiver slot (if the method is - * an instance method) and for the requirement that {@code long} and {@code double} + * an instance method) and for the requirement that {@link TypeKind#LONG + * long} and {@link TypeKind#DOUBLE double} * values require two slots. * * @param paramNo the index of the parameter @@ -115,25 +138,28 @@ public sealed interface CodeBuilder /** * {@return the local variable slot of a fresh local variable} This method * makes reasonable efforts to determine which slots are in use and which - * are not. When transforming a method, fresh locals begin at the {@code maxLocals} - * of the original method. For a method being built directly, fresh locals - * begin after the last parameter slot. - * - *

    If the current code builder is a "block" code builder provided by - * {@link #block(Consumer)}, {@link #ifThen(Consumer)}, or - * {@link #ifThenElse(Consumer, Consumer)}, at the end of the block, locals - * are reset to their value at the beginning of the block. + * are not. When transforming a method, fresh locals begin at the {@code + * maxLocals} of the original method. For a method being built directly, + * fresh locals begin after the last parameter slot. + *

    + * If the current code builder is a {@link BlockCodeBuilder}, at the end of + * the block, locals are reset to their value at the beginning of the block. * * @param typeKind the type of the local variable */ int allocateLocal(TypeKind typeKind); /** - * Apply a transform to the code built by a handler, directing results to this builder. + * Apply a transform to the code built by a handler, directing results to + * this builder. + * + * @apiNote + * This is similar to {@link #transform}, but this does not require the + * code elements to be viewed as a {@link CodeModel} first. * * @param transform the transform to apply to the code built by the handler - * @param handler the handler that receives a {@linkplain CodeBuilder} to - * build the code. + * @param handler the handler that receives a {@link CodeBuilder} to + * build the code * @return this builder */ default CodeBuilder transforming(CodeTransform transform, Consumer handler) { @@ -145,32 +171,36 @@ default CodeBuilder transforming(CodeTransform transform, Consumer } /** - * A builder for blocks of code. + * A builder for blocks of code. Its {@link #startLabel()} and {@link + * #endLabel()} do not enclose the entire method body, but from the start to + * the end of the block. * * @since 24 */ sealed interface BlockCodeBuilder extends CodeBuilder permits BlockCodeBuilderImpl { /** - * {@return the label locating where control is passed back to the parent block.} - * A branch to this label "break"'s out of the current block. + * {@return the label locating where control is passed back to the + * parent block} A branch to this label "break"'s out of the current + * block. *

    - * If an instruction occurring immediately after the built block's last instruction would - * be reachable from that last instruction, then a {@linkplain #goto_ goto} instruction - * targeting the "break" label is appended to the built block. + * If the last instruction in this block does not lead to the break + * label, Class-File API may append instructions to target the "break" + * label to the built block. */ Label breakLabel(); } /** - * Add a lexical block to the method being built. + * Adds a lexical block to the method being built. *

    - * Within this block, the {@link #startLabel()} and {@link #endLabel()} correspond - * to the start and end of the block, and the {@link BlockCodeBuilder#breakLabel()} - * also corresponds to the end of the block. + * Within this block, the {@link #startLabel()} and {@link #endLabel()} + * correspond to the start and end of the block, and the {@link + * BlockCodeBuilder#breakLabel()} also corresponds to the end of the block, + * or the cursor position immediately after this call in this builder. * - * @param handler handler that receives a {@linkplain BlockCodeBuilder} to - * generate the body of the lexical block. + * @param handler handler that receives a {@link BlockCodeBuilder} to + * generate the body of the lexical block * @return this builder */ default CodeBuilder block(Consumer handler) { @@ -183,33 +213,37 @@ default CodeBuilder block(Consumer handler) { } /** - * Add an "if-then" block that is conditional on the boolean value - * on top of the operand stack. + * Adds an "if-then" block that is conditional on the {@link TypeKind#BOOLEAN + * boolean} value on top of the operand stack. Control flow enters the + * "then" block if the value represents {@code true}. *

    - * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds to the - * end of that block. + * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds + * to the cursor position immediately after this call in this builder. * - * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to - * generate the body of the {@code if} + * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} + * to generate the body of the {@code if} * @return this builder + * @see #ifThen(Opcode, Consumer) */ default CodeBuilder ifThen(Consumer thenHandler) { return ifThen(Opcode.IFNE, thenHandler); } /** - * Add an "if-then" block that is conditional on the value(s) on top of the operand stack - * in accordance with the given opcode. + * Adds an "if-then" block that is conditional on the value(s) on top of the + * operand stack in accordance with the given opcode. Control flow enters + * the "then" block if the branching condition for {@code opcode} succeeds. *

    - * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds to the - * end of that block. + * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds + * to the cursor position immediately after this call in this builder. * - * @param opcode the operation code for a branch instructions that accepts one or two operands on the stack - * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to + * @param opcode the operation code for a branch instruction that accepts + * one or two operands on the stack + * @param thenHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the {@code if} * @return this builder - * @throws IllegalArgumentException if the operation code is not for a branch instruction that accepts - * one or two operands + * @throws IllegalArgumentException if the operation code is not for a + * branch instruction that accepts one or two operands */ default CodeBuilder ifThen(Opcode opcode, Consumer thenHandler) { @@ -227,17 +261,20 @@ default CodeBuilder ifThen(Opcode opcode, } /** - * Add an "if-then-else" block that is conditional on the boolean value - * on top of the operand stack. + * Adds an "if-then-else" block that is conditional on the {@link + * TypeKind#BOOLEAN boolean} value on top of the operand stack. Control + * flow enters the "then" block if the value represents {@code true}, and + * enters the "else" block otherwise. *

    - * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to the - * end of the "else" block. + * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to + * the cursor position immediately after this call in this builder. * - * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to + * @param thenHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the {@code if} - * @param elseHandler handler that receives a {@linkplain BlockCodeBuilder} to + * @param elseHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the {@code else} * @return this builder + * @see #ifThenElse(Opcode, Consumer, Consumer) */ default CodeBuilder ifThenElse(Consumer thenHandler, Consumer elseHandler) { @@ -245,20 +282,23 @@ default CodeBuilder ifThenElse(Consumer thenHandler, } /** - * Add an "if-then-else" block that is conditional on the value(s) on top of the operand stack - * in accordance with the given opcode. + * Adds an "if-then-else" block that is conditional on the value(s) on top + * of the operand stack in accordance with the given opcode. Control flow + * enters the "then" block if the branching condition for {@code opcode} + * succeeds, and enters the "else" block otherwise. *

    - * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to the - * end of the "else" block. + * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to + * the cursor position immediately after this call in this builder. * - * @param opcode the operation code for a branch instructions that accepts one or two operands on the stack - * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to - * generate the body of the {@code if} - * @param elseHandler handler that receives a {@linkplain BlockCodeBuilder} to - * generate the body of the {@code else} + * @param opcode the operation code for a branch instruction that accepts + * one or two operands on the stack + * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} + * to generate the body of the {@code if} + * @param elseHandler handler that receives a {@linkplain BlockCodeBuilder} + * to generate the body of the {@code else} * @return this builder - * @throws IllegalArgumentException if the operation code is not for a branch instruction that accepts - * one or two operands + * @throws IllegalArgumentException if the operation code is not for a + * branch instruction that accepts one or two operands */ default CodeBuilder ifThenElse(Opcode opcode, Consumer thenHandler, @@ -286,23 +326,29 @@ default CodeBuilder ifThenElse(Opcode opcode, * A builder to add catch blocks. * * @see #trying - * + * @see ExceptionCatch * @since 24 */ sealed interface CatchBuilder permits CatchBuilderImpl { /** * Adds a catch block that catches an exception of the given type. *

    - * The caught exception will be on top of the operand stack when the catch block is entered. + * The caught exception will be on top of the operand stack when the + * catch block is entered. *

    - * If the type of exception is {@code null} then the catch block catches all exceptions. + * The {@link BlockCodeBuilder#breakLabel()} for the catch block corresponds + * to the break label of the {@code tryHandler} block in {@link #trying}. + *

    + * If the type of exception is {@code null} then the catch block catches + * all exceptions. * - * @param exceptionType the type of exception to catch. - * @param catchHandler handler that receives a {@linkplain CodeBuilder} to - * generate the body of the catch block. + * @param exceptionType the type of exception to catch, may be {@code null} + * @param catchHandler handler that receives a {@link BlockCodeBuilder} to + * generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches an exception of the given type - * or {@code exceptionType} represents a primitive type + * @throws IllegalArgumentException if an existing catch block catches + * an exception of the given type or {@code exceptionType} + * represents a primitive type * @see #catchingMulti * @see #catchingAll */ @@ -311,15 +357,21 @@ sealed interface CatchBuilder permits CatchBuilderImpl { /** * Adds a catch block that catches exceptions of the given types. *

    - * The caught exception will be on top of the operand stack when the catch block is entered. + * The caught exception will be on top of the operand stack when the + * catch block is entered. + *

    + * The {@link BlockCodeBuilder#breakLabel()} for the catch block corresponds + * to the break label of the {@code tryHandler} block in {@link #trying}. *

    - * If the type of exception is {@code null} then the catch block catches all exceptions. + * If list of exception types is empty then the catch block catches all + * exceptions. * - * @param exceptionTypes the types of exception to catch. - * @param catchHandler handler that receives a {@linkplain CodeBuilder} to - * generate the body of the catch block. + * @param exceptionTypes the types of exception to catch + * @param catchHandler handler that receives a {@link BlockCodeBuilder} + * to generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches one or more exceptions of the given types. + * @throws IllegalArgumentException if an existing catch block catches + * one or more exceptions of the given types * @see #catching * @see #catchingAll */ @@ -328,11 +380,16 @@ sealed interface CatchBuilder permits CatchBuilderImpl { /** * Adds a "catch" block that catches all exceptions. *

    - * The caught exception will be on top of the operand stack when the catch block is entered. + * The {@link BlockCodeBuilder#breakLabel()} for the catch block corresponds + * to the break label of the {@code tryHandler} block in {@link #trying}. + *

    + * The caught exception will be on top of the operand stack when the + * catch block is entered. * - * @param catchAllHandler handler that receives a {@linkplain CodeBuilder} to - * generate the body of the catch block - * @throws IllegalArgumentException if an existing catch block catches all exceptions. + * @param catchAllHandler handler that receives a {@link BlockCodeBuilder} + * to generate the body of the catch block + * @throws IllegalArgumentException if an existing catch block catches + * all exceptions * @see #catching * @see #catchingMulti */ @@ -340,16 +397,23 @@ sealed interface CatchBuilder permits CatchBuilderImpl { } /** - * Adds a "try-catch" block comprising one try block and zero or more catch blocks. - * Exceptions thrown by instructions in the try block may be caught by catch blocks. + * Adds a "try-catch" block comprising one try block and zero or more catch + * blocks. Exceptions thrown by instructions in the try block may be caught + * by catch blocks. + *

    + * The {@link BlockCodeBuilder#breakLabel()} for the try block and all + * catch blocks in the {@code catchesHandler} correspond to the cursor + * position immediately after this call in this builder. * - * @param tryHandler handler that receives a {@linkplain CodeBuilder} to + * @param tryHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the try block. - * @param catchesHandler a handler that receives a {@linkplain CatchBuilder} - * to generate bodies of catch blocks. + * @param catchesHandler a handler that receives a {@link CatchBuilder} + * to generate bodies of catch blocks * @return this builder - * @throws IllegalArgumentException if the try block is empty. + * @throws IllegalArgumentException if the try block is empty * @see CatchBuilder + * @see ExceptionCatch + * @see #exceptionCatch */ default CodeBuilder trying(Consumer tryHandler, Consumer catchesHandler) { @@ -375,93 +439,119 @@ default CodeBuilder trying(Consumer tryHandler, // Base convenience methods /** - * Generate an instruction to load a value from a local variable + * Generates an instruction to load a value from a local variable. + * * @param tk the load type * @param slot the local variable slot * @return this builder - * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void} - * or {@code slot} is out of range + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID + * void} or {@code slot} is out of range + * @see LoadInstruction */ default CodeBuilder loadLocal(TypeKind tk, int slot) { return with(LoadInstruction.of(tk, slot)); } /** - * Generate an instruction to store a value to a local variable + * Generates an instruction to store a value to a local variable. + * * @param tk the store type * @param slot the local variable slot * @return this builder - * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void} - * or {@code slot} is out of range + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID + * void} or {@code slot} is out of range + * @see StoreInstruction */ default CodeBuilder storeLocal(TypeKind tk, int slot) { return with(StoreInstruction.of(tk, slot)); } /** - * Generate a branch instruction - * @see Opcode.Kind#BRANCH + * Generates a branch instruction. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set, the + * opcode has {@linkplain Opcode#sizeIfFixed() size} 3, and {@code target} + * cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param op the branch opcode * @param target the branch target * @return this builder + * @throws IllegalArgumentException if {@code op} is not of {@link + * Opcode.Kind#BRANCH} + * @see BranchInstruction */ default CodeBuilder branch(Opcode op, Label target) { return with(BranchInstruction.of(op, target)); } /** - * Generate return instruction + * Generates a return instruction. + * * @param tk the return type * @return this builder + * @see ReturnInstruction */ default CodeBuilder return_(TypeKind tk) { return with(ReturnInstruction.of(tk)); } /** - * Generate an instruction to access a field - * @see Opcode.Kind#FIELD_ACCESS + * Generates an instruction to access a field. + * * @param opcode the field access opcode * @param ref the field reference * @return this builder + * @throws IllegalArgumentException if {@code opcode} is not of {@link + * Opcode.Kind#FIELD_ACCESS} + * @see FieldInstruction */ default CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) { return with(FieldInstruction.of(opcode, ref)); } /** - * Generate an instruction to access a field - * @see Opcode.Kind#FIELD_ACCESS + * Generates an instruction to access a field. + * * @param opcode the field access opcode * @param owner the class * @param name the field name * @param type the field type * @return this builder + * @throws IllegalArgumentException if {@code opcode} is not of {@link + * Opcode.Kind#FIELD_ACCESS}, or {@code owner} is primitive + * @see FieldInstruction */ default CodeBuilder fieldAccess(Opcode opcode, ClassDesc owner, String name, ClassDesc type) { return fieldAccess(opcode, constantPool().fieldRefEntry(owner, name, type)); } /** - * Generate an instruction to invoke a method or constructor - * @see Opcode.Kind#INVOKE + * Generates an instruction to invoke a method. + * * @param opcode the invoke opcode * @param ref the interface method or method reference * @return this builder + * @throws IllegalArgumentException if {@code opcode} is not of {@link + * Opcode.Kind#INVOKE} + * @see InvokeInstruction */ default CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) { return with(InvokeInstruction.of(opcode, ref)); } /** - * Generate an instruction to invoke a method or constructor - * @see Opcode.Kind#INVOKE + * Generates an instruction to invoke a method. + * * @param opcode the invoke opcode * @param owner the class * @param name the method name * @param desc the method type - * @param isInterface the interface method invocation indication + * @param isInterface whether the owner class is an interface * @return this builder + * @throws IllegalArgumentException if {@code opcode} is not of {@link + * Opcode.Kind#INVOKE}, or {@code owner} is primitive + * @see InvokeInstruction */ default CodeBuilder invoke(Opcode opcode, ClassDesc owner, String name, MethodTypeDesc desc, boolean isInterface) { return invoke(opcode, @@ -470,9 +560,13 @@ default CodeBuilder invoke(Opcode opcode, ClassDesc owner, String name, MethodTy } /** - * Generate an instruction to load from an array + * Generates an instruction to load from an array. + * * @param tk the array element type * @return this builder + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID + * void} + * @see ArrayLoadInstruction */ default CodeBuilder arrayLoad(TypeKind tk) { Opcode opcode = BytecodeHelpers.arrayLoadOpcode(tk); @@ -480,9 +574,13 @@ default CodeBuilder arrayLoad(TypeKind tk) { } /** - * Generate an instruction to store into an array + * Generates an instruction to store into an array. + * * @param tk the array element type * @return this builder + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID + * void} + * @see ArrayStoreInstruction */ default CodeBuilder arrayStore(TypeKind tk) { Opcode opcode = BytecodeHelpers.arrayStoreOpcode(tk); @@ -490,12 +588,14 @@ default CodeBuilder arrayStore(TypeKind tk) { } /** - * Generate instruction(s) to convert {@code fromType} to {@code toType} + * Generates instruction(s) to convert {@code fromType} to {@code toType}. + * * @param fromType the source type * @param toType the target type * @return this builder - * @throws IllegalArgumentException for conversions of {@link TypeKind#VOID void} or - * {@link TypeKind#REFERENCE reference} + * @throws IllegalArgumentException for conversions of {@link TypeKind#VOID + * void} or {@link TypeKind#REFERENCE reference} + * @see ConvertInstruction */ default CodeBuilder conversion(TypeKind fromType, TypeKind toType) { var computationalFrom = fromType.asLoadable(); @@ -548,9 +648,11 @@ default CodeBuilder conversion(TypeKind fromType, TypeKind toType) { } /** - * Generate an instruction pushing a constant onto the operand stack - * @param value the constant value + * Generates an instruction pushing a constant onto the operand stack. + * + * @param value the constant value, may be {@code null} * @return this builder + * @see ConstantInstruction */ default CodeBuilder loadConstant(ConstantDesc value) { //avoid switch expressions here @@ -567,11 +669,13 @@ default CodeBuilder loadConstant(ConstantDesc value) { /** - * Generate an instruction pushing a constant int value onto the operand stack. - * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Integer.valueOf(value))}. + * Generates an instruction pushing a constant {@link TypeKind#INT int} + * value onto the operand stack. This is equivalent to {@link + * #loadConstant(ConstantDesc) loadConstant(Integer.valueOf(value))}. + * * @param value the int value * @return this builder - * @since 24 + * @see ConstantInstruction */ default CodeBuilder loadConstant(int value) { return switch (value) { @@ -589,11 +693,13 @@ default CodeBuilder loadConstant(int value) { } /** - * Generate an instruction pushing a constant long value onto the operand stack. - * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Long.valueOf(value))}. + * Generates an instruction pushing a constant {@link TypeKind#LONG long} + * value onto the operand stack. This is equivalent to {@link + * #loadConstant(ConstantDesc) loadConstant(Long.valueOf(value))}. + * * @param value the long value * @return this builder - * @since 24 + * @see ConstantInstruction */ default CodeBuilder loadConstant(long value) { return value == 0l ? lconst_0() @@ -602,11 +708,16 @@ default CodeBuilder loadConstant(long value) { } /** - * Generate an instruction pushing a constant float value onto the operand stack. - * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Float.valueOf(value))}. + * Generates an instruction pushing a constant {@link TypeKind#FLOAT float} + * value onto the operand stack. This is equivalent to {@link + * #loadConstant(ConstantDesc) loadConstant(Float.valueOf(value))}. + *

    + * All NaN values of the {@code float} may or may not be collapsed + * into a single {@linkplain Float#NaN "canonical" NaN value}. + * * @param value the float value * @return this builder - * @since 24 + * @see ConstantInstruction */ default CodeBuilder loadConstant(float value) { return Float.floatToRawIntBits(value) == 0 ? fconst_0() @@ -616,11 +727,16 @@ default CodeBuilder loadConstant(float value) { } /** - * Generate an instruction pushing a constant double value onto the operand stack. - * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Double.valueOf(value))}. + * Generates an instruction pushing a constant {@link TypeKind#DOUBLE double} + * value onto the operand stack. This is equivalent to {@link + * #loadConstant(ConstantDesc) loadConstant(Double.valueOf(value))}. + *

    + * All NaN values of the {@code double} may or may not be collapsed + * into a single {@linkplain Double#NaN "canonical" NaN value}. + * * @param value the double value * @return this builder - * @since 24 + * @see ConstantInstruction */ default CodeBuilder loadConstant(double value) { return Double.doubleToRawLongBits(value) == 0l ? dconst_0() @@ -629,8 +745,10 @@ default CodeBuilder loadConstant(double value) { } /** - * Generate a do nothing instruction + * Generates a do-nothing instruction. + * * @return this builder + * @see NopInstruction */ default CodeBuilder nop() { return with(NopInstruction.of()); @@ -639,8 +757,11 @@ default CodeBuilder nop() { // Base pseudo-instruction builder methods /** - * Create new label bound with current position + * Creates a new label bound at the current position. + * * @return this builder + * @see #newLabel() + * @see #labelBinding */ default Label newBoundLabel() { var label = newLabel(); @@ -649,54 +770,86 @@ default Label newBoundLabel() { } /** - * Bind label with current position + * Binds a label to the current position. + * + * @apiNote + * The label to bind does not have to be {@linkplain #newLabel() from this + * builder}; it can be from another parsed {@link CodeModel}. + * * @param label the label * @return this builder + * @see LabelTarget */ default CodeBuilder labelBinding(Label label) { return with((LabelImpl) label); } /** - * Declare a source line number of the current builder position + * Declares a source line number beginning at the current position. + *

    + * This call may be ignored according to {@link ClassFile.LineNumbersOption}. + * * @param line the line number * @return this builder + * @see LineNumber */ default CodeBuilder lineNumber(int line) { return with(LineNumber.of(line)); } /** - * Declare an exception table entry + * Declares an exception table entry. + *

    + * This call may be ignored if any of the argument labels is not {@linkplain + * #labelBinding bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} + * is set. + * * @param start the try block start * @param end the try block end * @param handler the exception handler start - * @param catchType the catch type or null to catch all exceptions and errors + * @param catchType the catch type, may be {@code null} to catch all exceptions and errors * @return this builder + * @see ExceptionCatch + * @see CodeBuilder#trying */ default CodeBuilder exceptionCatch(Label start, Label end, Label handler, ClassEntry catchType) { return with(ExceptionCatch.of(handler, start, end, Optional.ofNullable(catchType))); } /** - * Declare an exception table entry + * Declares an exception table entry. + *

    + * This call may be ignored if any of the argument labels is not {@linkplain + * #labelBinding bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} + * is set. + * * @param start the try block start * @param end the try block end * @param handler the exception handler start * @param catchType the optional catch type, empty to catch all exceptions and errors * @return this builder + * @see ExceptionCatch + * @see CodeBuilder#trying */ default CodeBuilder exceptionCatch(Label start, Label end, Label handler, Optional catchType) { return with(ExceptionCatch.of(handler, start, end, catchType)); } /** - * Declare an exception table entry + * Declares an exception table entry. + *

    + * This call may be ignored if any of the argument labels is not {@linkplain + * #labelBinding bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} + * is set. + * * @param start the try block start * @param end the try block end * @param handler the exception handler start * @param catchType the catch type * @return this builder + * @throws IllegalArgumentException if {@code catchType} is primitive + * @see ExceptionCatch + * @see CodeBuilder#trying */ default CodeBuilder exceptionCatch(Label start, Label end, Label handler, ClassDesc catchType) { requireNonNull(catchType); @@ -704,31 +857,49 @@ default CodeBuilder exceptionCatch(Label start, Label end, Label handler, ClassD } /** - * Declare an exception table entry catching all exceptions and errors + * Declares an exception table entry catching all exceptions and errors. + *

    + * This call may be ignored if any of the argument labels is not {@linkplain + * #labelBinding bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} + * is set. + * * @param start the try block start * @param end the try block end * @param handler the exception handler start * @return this builder + * @see ExceptionCatch + * @see CodeBuilder#trying */ default CodeBuilder exceptionCatchAll(Label start, Label end, Label handler) { return with(ExceptionCatch.of(handler, start, end)); } /** - * Declare a character range entry + * Declares a character range entry. + *

    + * This call may be ignored if {@link ClassFile.DebugElementsOption#DROP_DEBUG} + * is set, or if any of the argument labels is not {@linkplain #labelBinding + * bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} is set. + * * @param startScope the start scope of the character range * @param endScope the end scope of the character range * @param characterRangeStart the encoded start of the character range region (inclusive) * @param characterRangeEnd the encoded end of the character range region (exclusive) * @param flags the flags word, indicating the kind of range * @return this builder + * @see CharacterRange */ default CodeBuilder characterRange(Label startScope, Label endScope, int characterRangeStart, int characterRangeEnd, int flags) { return with(CharacterRange.of(startScope, endScope, characterRangeStart, characterRangeEnd, flags)); } /** - * Declare a local variable entry + * Declares a local variable entry. + *

    + * This call may be ignored if {@link ClassFile.DebugElementsOption#DROP_DEBUG} + * is set, or if any of the argument labels is not {@linkplain #labelBinding + * bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} is set. + * * @param slot the local variable slot * @param nameEntry the variable name * @param descriptorEntry the variable descriptor @@ -736,13 +907,19 @@ default CodeBuilder characterRange(Label startScope, Label endScope, int charact * @param endScope the end scope of the variable * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see LocalVariable */ default CodeBuilder localVariable(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry, Label startScope, Label endScope) { return with(LocalVariable.of(slot, nameEntry, descriptorEntry, startScope, endScope)); } /** - * Declare a local variable entry + * Declares a local variable entry. + *

    + * This call may be ignored if {@link ClassFile.DebugElementsOption#DROP_DEBUG} + * is set, or if any of the argument labels is not {@linkplain #labelBinding + * bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} is set. + * * @param slot the local variable slot * @param name the variable name * @param descriptor the variable descriptor @@ -750,6 +927,7 @@ default CodeBuilder localVariable(int slot, Utf8Entry nameEntry, Utf8Entry descr * @param endScope the end scope of the variable * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see LocalVariable */ default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, Label startScope, Label endScope) { return localVariable(slot, @@ -759,7 +937,17 @@ default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, L } /** - * Declare a local variable type entry + * Declares a local variable type entry. + *

    + * This call may be ignored if {@link ClassFile.DebugElementsOption#DROP_DEBUG} + * is set, or if any of the argument labels is not {@linkplain #labelBinding + * bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} is set. + * + * @apiNote + * When a local variable type entry is declared, a local variable entry with + * the descriptor derived from erasure (JLS {@jls 4.6}) of the signature + * should be declared as well. + * * @param slot the local variable slot * @param nameEntry the variable name * @param signatureEntry the variable signature @@ -767,13 +955,24 @@ default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, L * @param endScope the end scope of the variable * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see LocalVariableType */ default CodeBuilder localVariableType(int slot, Utf8Entry nameEntry, Utf8Entry signatureEntry, Label startScope, Label endScope) { return with(LocalVariableType.of(slot, nameEntry, signatureEntry, startScope, endScope)); } /** - * Declare a local variable type entry + * Declares a local variable type entry. + *

    + * This call may be ignored if {@link ClassFile.DebugElementsOption#DROP_DEBUG} + * is set, or if any of the argument labels is not {@linkplain #labelBinding + * bound} and {@link ClassFile.DeadLabelsOption#DROP_DEAD_LABELS} is set. + * + * @apiNote + * When a local variable type entry is declared, a local variable entry with + * the descriptor derived from erasure (JLS {@jls 4.6}) of the signature + * should be declared as well. + * * @param slot the local variable slot * @param name the variable name * @param signature the variable signature @@ -781,6 +980,7 @@ default CodeBuilder localVariableType(int slot, Utf8Entry nameEntry, Utf8Entry s * @param endScope the end scope of the variable * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see LocalVariableType */ default CodeBuilder localVariableType(int slot, String name, Signature signature, Label startScope, Label endScope) { return localVariableType(slot, @@ -792,971 +992,1491 @@ default CodeBuilder localVariableType(int slot, String name, Signature signature // Bytecode conveniences /** - * Generate an instruction pushing the null object reference onto the operand stack + * Generates an instruction pushing the null object {@link TypeKind#REFERENCE + * reference} onto the operand stack. + * * @return this builder + * @see Opcode#ACONST_NULL + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder aconst_null() { return with(ConstantInstruction.ofIntrinsic(Opcode.ACONST_NULL)); } /** - * Generate an instruction to load a reference from an array + * Generates an instruction to load from a {@link TypeKind#REFERENCE + * reference} array. + * * @return this builder + * @see Opcode#AALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder aaload() { return arrayLoad(TypeKind.REFERENCE); } /** - * Generate an instruction to store into a reference array + * Generates an instruction to store into a {@link TypeKind#REFERENCE + * reference} array. + * * @return this builder + * @see Opcode#AASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder aastore() { return arrayStore(TypeKind.REFERENCE); } /** - * Generate an instruction to load a reference from a local variable - * - *

    This may also generate {@code aload_} and - * {@code wide aload} instructions. + * Generates an instruction to load a {@link TypeKind#REFERENCE reference} + * from a local variable. + *

    + * This may also generate {@link Opcode#ALOAD_0 aload_<N>} and {@link + * Opcode#ALOAD_W wide aload} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#ALOAD + * @see #loadLocal + * @see LoadInstruction */ default CodeBuilder aload(int slot) { return loadLocal(TypeKind.REFERENCE, slot); } /** - * Generate an instruction to create a new array of reference + * Generates an instruction to create a new array of {@link TypeKind#REFERENCE + * reference}. + * * @param classEntry the component type * @return this builder + * @see Opcode#ANEWARRAY + * @see NewReferenceArrayInstruction */ default CodeBuilder anewarray(ClassEntry classEntry) { return with(NewReferenceArrayInstruction.of(classEntry)); } /** - * Generate an instruction to create a new array of reference + * Generates an instruction to create a new array of {@link TypeKind#REFERENCE + * reference}. + * * @param className the component type * @return this builder * @throws IllegalArgumentException if {@code className} represents a primitive type + * @see Opcode#ANEWARRAY + * @see NewReferenceArrayInstruction */ default CodeBuilder anewarray(ClassDesc className) { return anewarray(constantPool().classEntry(className)); } /** - * Generate an instruction to return a reference from the method + * Generates an instruction to return a {@link TypeKind#REFERENCE reference} + * from this method. + * * @return this builder + * @see Opcode#ARETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder areturn() { return return_(TypeKind.REFERENCE); } /** - * Generate an instruction to get length of an array + * Generates an instruction to get the length of an array. + * * @return this builder + * @see Opcode#ARRAYLENGTH + * @see OperatorInstruction */ default CodeBuilder arraylength() { return with(OperatorInstruction.of(Opcode.ARRAYLENGTH)); } /** - * Generate an instruction to store a reference into a local variable - * - *

    This may also generate {@code astore_} and - * {@code wide astore} instructions. + * Generates an instruction to store a {@link TypeKind#REFERENCE reference} + * into a local variable. Such an instruction can also store a {@link + * TypeKind##returnAddress returnAddress}. + *

    + * This may also generate {@link Opcode#ASTORE_0 astore_<N>} and + * {@link Opcode#ASTORE_W wide astore} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#ASTORE + * @see #storeLocal + * @see StoreInstruction */ default CodeBuilder astore(int slot) { return storeLocal(TypeKind.REFERENCE, slot); } /** - * Generate an instruction to throw an exception or error + * Generates an instruction to throw an exception or error. + * * @return this builder + * @see Opcode#ATHROW + * @see ThrowInstruction */ default CodeBuilder athrow() { return with(ThrowInstruction.of()); } /** - * Generate an instruction to load a byte from a array + * Generates an instruction to load from a {@link TypeKind#BYTE byte} or + * {@link TypeKind#BOOLEAN boolean} array. + * * @return this builder + * @see Opcode#BALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder baload() { return arrayLoad(TypeKind.BYTE); } /** - * Generate an instruction to store into a byte array + * Generates an instruction to store into a {@link TypeKind#BYTE byte} or + * {@link TypeKind#BOOLEAN boolean} array. + * * @return this builder + * @see Opcode#BASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder bastore() { return arrayStore(TypeKind.BYTE); } /** - * Generate an instruction pushing an int in the range of byte onto the operand stack. + * Generates an instruction pushing an {@link TypeKind#INT int} in the range + * of {@link TypeKind#BYTE byte} ({@code [-128, 127]}) onto the operand + * stack. + * * @param b the int in the range of byte * @return this builder * @throws IllegalArgumentException if {@code b} is out of range of byte + * @see Opcode#BIPUSH + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder bipush(int b) { return with(ConstantInstruction.ofArgument(Opcode.BIPUSH, b)); } /** - * Generate an instruction to load a char from an array + * Generates an instruction to load from a {@link TypeKind#CHAR char} array. + * * @return this builder + * @see Opcode#CALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder caload() { return arrayLoad(TypeKind.CHAR); } /** - * Generate an instruction to store into a char array + * Generates an instruction to store into a {@link TypeKind#CHAR char} array. + * * @return this builder + * @see Opcode#CASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder castore() { return arrayStore(TypeKind.CHAR); } /** - * Generate an instruction to check whether an object is of the given type + * Generates an instruction to check whether an object is of the given type, + * throwing a {@link ClassCastException} if the check fails. + * * @param type the object type * @return this builder + * @see Opcode#CHECKCAST + * @see TypeCheckInstruction */ default CodeBuilder checkcast(ClassEntry type) { return with(TypeCheckInstruction.of(Opcode.CHECKCAST, type)); } /** - * Generate an instruction to check whether an object is of the given type + * Generates an instruction to check whether an object is of the given type, + * throwing a {@link ClassCastException} if the check fails. + * * @param type the object type * @return this builder * @throws IllegalArgumentException if {@code type} represents a primitive type + * @see Opcode#CHECKCAST + * @see TypeCheckInstruction */ default CodeBuilder checkcast(ClassDesc type) { return checkcast(constantPool().classEntry(type)); } /** - * Generate an instruction to convert a double into a float + * Generates an instruction to convert a {@link TypeKind#DOUBLE double} into + * a {@link TypeKind#FLOAT float}. + * * @return this builder + * @see Opcode#D2F + * @see ConvertInstruction */ default CodeBuilder d2f() { return with(ConvertInstruction.of(Opcode.D2F)); } /** - * Generate an instruction to convert a double into an int + * Generates an instruction to convert a {@link TypeKind#DOUBLE double} into + * an {@link TypeKind#INT int}. + * * @return this builder + * @see Opcode#D2I + * @see ConvertInstruction */ default CodeBuilder d2i() { return with(ConvertInstruction.of(Opcode.D2I)); } /** - * Generate an instruction to convert a double into a long + * Generates an instruction to convert a {@link TypeKind#DOUBLE double} into + * a {@link TypeKind#LONG long}. + * * @return this builder + * @see Opcode#D2L + * @see ConvertInstruction */ default CodeBuilder d2l() { return with(ConvertInstruction.of(Opcode.D2L)); } /** - * Generate an instruction to add a double + * Generates an instruction to add two {@link TypeKind#DOUBLE doubles}. + * * @return this builder + * @see Opcode#DADD + * @see OperatorInstruction */ default CodeBuilder dadd() { return with(OperatorInstruction.of(Opcode.DADD)); } /** - * Generate an instruction to load a double from an array + * Generates an instruction to load from a {@link TypeKind#DOUBLE double} + * array. + * * @return this builder + * @see Opcode#DALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder daload() { return arrayLoad(TypeKind.DOUBLE); } /** - * Generate an instruction to store into a double array + * Generates an instruction to store into a {@link TypeKind#DOUBLE double} + * array. + * * @return this builder + * @see Opcode#DASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder dastore() { return arrayStore(TypeKind.DOUBLE); } /** - * Generate an instruction to add a double + * Generates an instruction to compare two {@link TypeKind#DOUBLE doubles}, + * producing {@code 1} if any operand is {@link Double#isNaN(double) NaN}. + * * @return this builder + * @see Opcode#DCMPG + * @see OperatorInstruction */ default CodeBuilder dcmpg() { return with(OperatorInstruction.of(Opcode.DCMPG)); } /** - * Generate an instruction to compare doubles + * Generates an instruction to compare two {@link TypeKind#DOUBLE doubles}, + * producing {@code -1} if any operand is {@link Double#isNaN(double) NaN}. + * * @return this builder + * @see Opcode#DCMPL + * @see OperatorInstruction */ default CodeBuilder dcmpl() { return with(OperatorInstruction.of(Opcode.DCMPL)); } /** - * Generate an instruction pushing double constant 0 onto the operand stack + * Generates an instruction pushing {@link TypeKind#DOUBLE double} constant + * 0 onto the operand stack. + * * @return this builder + * @see Opcode#DCONST_0 + * @see #loadConstant(double) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder dconst_0() { return with(ConstantInstruction.ofIntrinsic(Opcode.DCONST_0)); } /** - * Generate an instruction pushing double constant 1 onto the operand stack + * Generates an instruction pushing {@link TypeKind#DOUBLE double} constant + * 1 onto the operand stack. + * * @return this builder + * @see Opcode#DCONST_1 + * @see #loadConstant(double) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder dconst_1() { return with(ConstantInstruction.ofIntrinsic(Opcode.DCONST_1)); } /** - * Generate an instruction to divide doubles + * Generates an instruction to divide {@link TypeKind#DOUBLE doubles}. + * * @return this builder + * @see Opcode#DDIV + * @see OperatorInstruction */ default CodeBuilder ddiv() { return with(OperatorInstruction.of(Opcode.DDIV)); } /** - * Generate an instruction to load a double from a local variable - * - *

    This may also generate {@code dload_} and - * {@code wide dload} instructions. + * Generates an instruction to load a {@link TypeKind#DOUBLE double} from a + * local variable. + *

    + * This may also generate {@link Opcode#DLOAD_0 dload_<N>} and {@link + * Opcode#DLOAD_W wide dload} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#DLOAD + * @see #loadLocal(TypeKind, int) + * @see LoadInstruction */ default CodeBuilder dload(int slot) { return loadLocal(TypeKind.DOUBLE, slot); } /** - * Generate an instruction to multiply doubles + * Generates an instruction to multiply {@link TypeKind#DOUBLE doubles}. + * * @return this builder + * @see Opcode#DMUL + * @see OperatorInstruction */ default CodeBuilder dmul() { return with(OperatorInstruction.of(Opcode.DMUL)); } /** - * Generate an instruction to negate a double + * Generates an instruction to negate a {@link TypeKind#DOUBLE double}. + * * @return this builder + * @see Opcode#DNEG + * @see OperatorInstruction */ default CodeBuilder dneg() { return with(OperatorInstruction.of(Opcode.DNEG)); } /** - * Generate an instruction to calculate double remainder + * Generates an instruction to calculate {@link TypeKind#DOUBLE double} + * remainder. + * * @return this builder + * @see Opcode#DREM + * @see OperatorInstruction */ default CodeBuilder drem() { return with(OperatorInstruction.of(Opcode.DREM)); } /** - * Generate an instruction to return a double from the method + * Generates an instruction to return a {@link TypeKind#DOUBLE double} from + * this method. + * * @return this builder + * @see Opcode#DRETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder dreturn() { return return_(TypeKind.DOUBLE); } /** - * Generate an instruction to store a double into a local variable - * - *

    This may also generate {@code dstore_} and - * {@code wide dstore} instructions. + * Generates an instruction to store a {@link TypeKind#DOUBLE double} into a + * local variable. + *

    + * This may also generate {@link Opcode#DSTORE_0 dstore_<N>} and + * {@link Opcode#DSTORE_W wide dstore} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#DSTORE + * @see #storeLocal(TypeKind, int) + * @see StoreInstruction */ default CodeBuilder dstore(int slot) { return storeLocal(TypeKind.DOUBLE, slot); } /** - * Generate an instruction to subtract doubles + * Generates an instruction to subtract {@link TypeKind#DOUBLE doubles}. + * * @return this builder + * @see Opcode#DSUB + * @see OperatorInstruction */ default CodeBuilder dsub() { return with(OperatorInstruction.of(Opcode.DSUB)); } /** - * Generate an instruction to duplicate the top operand stack value + * Generates an instruction to duplicate the top operand stack value. + * * @return this builder + * @see Opcode#DUP + * @see StackInstruction */ default CodeBuilder dup() { return with(StackInstruction.of(Opcode.DUP)); } /** - * Generate an instruction to duplicate the top one or two operand stack value + * Generates an instruction to duplicate the top one or two operand stack + * value. + * * @return this builder + * @see Opcode#DUP2 + * @see StackInstruction */ default CodeBuilder dup2() { return with(StackInstruction.of(Opcode.DUP2)); } /** - * Generate an instruction to duplicate the top one or two operand stack values and insert two or three - * values down + * Generates an instruction to duplicate the top one or two operand stack + * values and insert two or three values down. + * * @return this builder + * @see Opcode#DUP2_X1 + * @see StackInstruction */ default CodeBuilder dup2_x1() { return with(StackInstruction.of(Opcode.DUP2_X1)); } /** - * Generate an instruction to duplicate the top one or two operand stack values and insert two, three, - * or four values down + * Generates an instruction to duplicate the top one or two operand stack + * values and insert two, three, or four values down. + * * @return this builder + * @see Opcode#DUP2_X2 + * @see StackInstruction */ default CodeBuilder dup2_x2() { return with(StackInstruction.of(Opcode.DUP2_X2)); } /** - * Generate an instruction to duplicate the top operand stack value and insert two values down + * Generates an instruction to duplicate the top operand stack value and + * insert two values down. + * * @return this builder + * @see Opcode#DUP_X1 + * @see StackInstruction */ default CodeBuilder dup_x1() { return with(StackInstruction.of(Opcode.DUP_X1)); } /** - * Generate an instruction to duplicate the top operand stack value and insert two or three values down + * Generates an instruction to duplicate the top operand stack value and + * insert two or three values down. + * * @return this builder + * @see Opcode#DUP_X2 + * @see StackInstruction */ default CodeBuilder dup_x2() { return with(StackInstruction.of(Opcode.DUP_X2)); } /** - * Generate an instruction to convert a float into a double + * Generates an instruction to convert a {@link TypeKind#FLOAT float} into a + * {@link TypeKind#DOUBLE double}. + * * @return this builder + * @see Opcode#F2D + * @see ConvertInstruction */ default CodeBuilder f2d() { return with(ConvertInstruction.of(Opcode.F2D)); } /** - * Generate an instruction to convert a float into an int + * Generates an instruction to convert a {@link TypeKind#FLOAT float} into + * an {@link TypeKind#INT int}. + * * @return this builder + * @see Opcode#F2I + * @see ConvertInstruction */ default CodeBuilder f2i() { return with(ConvertInstruction.of(Opcode.F2I)); } /** - * Generate an instruction to convert a float into a long + * Generates an instruction to convert a {@link TypeKind#FLOAT float} into a + * {@link TypeKind#LONG long}. + * * @return this builder + * @see Opcode#F2L + * @see ConvertInstruction */ default CodeBuilder f2l() { return with(ConvertInstruction.of(Opcode.F2L)); } /** - * Generate an instruction to add a float + * Generates an instruction to add two {@link TypeKind#FLOAT floats}. + * * @return this builder + * @see Opcode#FADD + * @see OperatorInstruction */ default CodeBuilder fadd() { return with(OperatorInstruction.of(Opcode.FADD)); } /** - * Generate an instruction to load a float from an array + * Generates an instruction to load from a {@link TypeKind#FLOAT float} + * array. + * * @return this builder + * @see Opcode#FALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder faload() { return arrayLoad(TypeKind.FLOAT); } /** - * Generate an instruction to store into a float array + * Generates an instruction to store into a {@link TypeKind#FLOAT float} + * array. + * * @return this builder + * @see Opcode#FASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder fastore() { return arrayStore(TypeKind.FLOAT); } /** - * Generate an instruction to compare floats + * Generates an instruction to compare {@link TypeKind#FLOAT floats}, + * producing {@code 1} if any operand is {@link Float#isNaN(float) NaN}. + * * @return this builder + * @see Opcode#FCMPG + * @see OperatorInstruction */ default CodeBuilder fcmpg() { return with(OperatorInstruction.of(Opcode.FCMPG)); } /** - * Generate an instruction to compare floats + * Generates an instruction to compare {@link TypeKind#FLOAT floats}, + * producing {@code -1} if any operand is {@link Float#isNaN(float) NaN}. + * * @return this builder + * @see Opcode#FCMPL + * @see OperatorInstruction */ default CodeBuilder fcmpl() { return with(OperatorInstruction.of(Opcode.FCMPL)); } /** - * Generate an instruction pushing float constant 0 onto the operand stack + * Generates an instruction pushing {@link TypeKind#FLOAT float} constant 0 + * onto the operand stack. + * * @return this builder + * @see Opcode#FCONST_0 + * @see #loadConstant(float) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder fconst_0() { return with(ConstantInstruction.ofIntrinsic(Opcode.FCONST_0)); } /** - * Generate an instruction pushing float constant 1 onto the operand stack + * Generates an instruction pushing {@link TypeKind#FLOAT float} constant 1 + * onto the operand stack. + * * @return this builder + * @see Opcode#FCONST_1 + * @see #loadConstant(float) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder fconst_1() { return with(ConstantInstruction.ofIntrinsic(Opcode.FCONST_1)); } /** - * Generate an instruction pushing float constant 2 onto the operand stack + * Generates an instruction pushing {@link TypeKind#FLOAT float} constant 2 + * onto the operand stack. + * * @return this builder + * @see Opcode#FCONST_2 + * @see #loadConstant(float) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder fconst_2() { return with(ConstantInstruction.ofIntrinsic(Opcode.FCONST_2)); } /** - * Generate an instruction to divide floats + * Generates an instruction to divide {@link TypeKind#FLOAT floats}. + * * @return this builder + * @see Opcode#FDIV + * @see OperatorInstruction */ default CodeBuilder fdiv() { return with(OperatorInstruction.of(Opcode.FDIV)); } /** - * Generate an instruction to load a float from a local variable - * - *

    This may also generate {@code fload_} and - * {@code wide fload} instructions. + * Generates an instruction to load a {@link TypeKind#FLOAT float} from a + * local variable. + *

    + * This may also generate {@link Opcode#FLOAD_0 fload_<N>} and {@link + * Opcode#FLOAD_W wide fload} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#FLOAD + * @see #loadLocal(TypeKind, int) + * @see LoadInstruction */ default CodeBuilder fload(int slot) { return loadLocal(TypeKind.FLOAT, slot); } /** - * Generate an instruction to multiply floats + * Generates an instruction to multiply {@link TypeKind#FLOAT floats}. + * * @return this builder + * @see Opcode#FMUL + * @see OperatorInstruction */ default CodeBuilder fmul() { return with(OperatorInstruction.of(Opcode.FMUL)); } /** - * Generate an instruction to negate a float + * Generates an instruction to negate a {@link TypeKind#FLOAT float}. + * * @return this builder + * @see Opcode#FNEG + * @see OperatorInstruction */ default CodeBuilder fneg() { return with(OperatorInstruction.of(Opcode.FNEG)); } /** - * Generate an instruction to calculate floats remainder + * Generates an instruction to calculate {@link TypeKind#FLOAT floats} + * remainder. + * * @return this builder + * @see Opcode#FREM + * @see OperatorInstruction */ default CodeBuilder frem() { return with(OperatorInstruction.of(Opcode.FREM)); } /** - * Generate an instruction to return a float from the method + * Generates an instruction to return a {@link TypeKind#FLOAT float} from + * this method. + * * @return this builder + * @see Opcode#FRETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder freturn() { return return_(TypeKind.FLOAT); } /** - * Generate an instruction to store a float into a local variable - * - *

    This may also generate {@code fstore_} and - * {@code wide fstore} instructions. + * Generates an instruction to store a {@link TypeKind#FLOAT float} into a + * local variable. + *

    + * This may also generate {@link Opcode#FSTORE_0 fstore_<N>} and + * {@link Opcode#FSTORE_W wide fstore} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#FSTORE + * @see #storeLocal(TypeKind, int) + * @see StoreInstruction */ default CodeBuilder fstore(int slot) { return storeLocal(TypeKind.FLOAT, slot); } /** - * Generate an instruction to subtract floats + * Generates an instruction to subtract {@link TypeKind#FLOAT floats}. + * * @return this builder + * @see Opcode#FSUB + * @see OperatorInstruction */ default CodeBuilder fsub() { return with(OperatorInstruction.of(Opcode.FSUB)); } /** - * Generate an instruction to fetch field from an object + * Generates an instruction to fetch field from an object. + * * @param ref the field reference * @return this builder + * @see Opcode#GETFIELD + * @see #fieldAccess(Opcode, FieldRefEntry) + * @see FieldInstruction */ default CodeBuilder getfield(FieldRefEntry ref) { return fieldAccess(Opcode.GETFIELD, ref); } /** - * Generate an instruction to fetch field from an object + * Generates an instruction to fetch field from an object. + * * @param owner the owner class * @param name the field name * @param type the field type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#GETFIELD + * @see #fieldAccess(Opcode, ClassDesc, String, ClassDesc) + * @see FieldInstruction */ default CodeBuilder getfield(ClassDesc owner, String name, ClassDesc type) { return fieldAccess(Opcode.GETFIELD, owner, name, type); } /** - * Generate an instruction to get static field from a class + * Generates an instruction to get static field from a class or interface. + * * @param ref the field reference * @return this builder + * @see Opcode#GETSTATIC + * @see #fieldAccess(Opcode, FieldRefEntry) + * @see FieldInstruction */ default CodeBuilder getstatic(FieldRefEntry ref) { return fieldAccess(Opcode.GETSTATIC, ref); } /** - * Generate an instruction to get static field from a class + * Generates an instruction to get static field from a class or interface. + * * @param owner the owner class * @param name the field name * @param type the field type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#GETSTATIC + * @see #fieldAccess(Opcode, ClassDesc, String, ClassDesc) + * @see FieldInstruction */ default CodeBuilder getstatic(ClassDesc owner, String name, ClassDesc type) { return fieldAccess(Opcode.GETSTATIC, owner, name, type); } /** - * Generate an instruction to branch always + * Generates an instruction to branch always. + *

    + * This may also generate {@link Opcode#GOTO_W goto_w} instructions if + * {@link ShortJumpsOption#FIX_SHORT_JUMPS} is set. * - *

    This may also generate {@code goto_w} instructions if the {@link - * ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS FIX_SHORT_JUMPS} option - * is set. - * - * @apiNote The instruction's name is {@code goto}, which coincides with a - * reserved keyword of the Java programming language, thus this method is - * named with an extra {@code _} suffix instead. + * @apiNote + * The instruction's name is {@code goto}, which coincides with a reserved + * keyword of the Java programming language, thus this method is named with + * an extra {@code _} suffix instead. * * @param target the branch target * @return this builder + * @see Opcode#GOTO + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder goto_(Label target) { return branch(Opcode.GOTO, target); } /** - * Generate an instruction to branch always with wide index + * Generates an instruction to branch always with wide index. + * * @param target the branch target * @return this builder + * @see Opcode#GOTO_W + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder goto_w(Label target) { return branch(Opcode.GOTO_W, target); } /** - * Generate an instruction to convert an int into a byte + * Generates an instruction to truncate an {@link TypeKind#INT int} into the + * range of {@link TypeKind#BYTE byte} and sign-extend it. + * * @return this builder + * @see Opcode#I2B + * @see ConvertInstruction */ default CodeBuilder i2b() { return with(ConvertInstruction.of(Opcode.I2B)); } /** - * Generate an instruction to convert an int into a char + * Generates an instruction to truncate an {@link TypeKind#INT int} into the + * range of {@link TypeKind#CHAR char} and zero-extend it. + * * @return this builder + * @see Opcode#I2C + * @see ConvertInstruction */ default CodeBuilder i2c() { return with(ConvertInstruction.of(Opcode.I2C)); } /** - * Generate an instruction to convert an int into a double + * Generates an instruction to convert an {@link TypeKind#INT int} into a + * {@link TypeKind#DOUBLE double}. + * * @return this builder + * @see Opcode#I2D + * @see ConvertInstruction */ default CodeBuilder i2d() { return with(ConvertInstruction.of(Opcode.I2D)); } /** - * Generate an instruction to convert an int into a float + * Generates an instruction to convert an {@link TypeKind#INT int} into a + * {@link TypeKind#FLOAT float}. + * * @return this builder + * @see Opcode#I2F + * @see ConvertInstruction */ default CodeBuilder i2f() { return with(ConvertInstruction.of(Opcode.I2F)); } /** - * Generate an instruction to convert an int into a long + * Generates an instruction to convert an {@link TypeKind#INT int} into a + * {@link TypeKind#LONG long}. + * * @return this builder + * @see Opcode#I2L + * @see ConvertInstruction */ default CodeBuilder i2l() { return with(ConvertInstruction.of(Opcode.I2L)); } /** - * Generate an instruction to convert an int into a short + * Generates an instruction to truncate an {@link TypeKind#INT int} into the + * range of {@link TypeKind#SHORT short} and sign-extend it. + * * @return this builder + * @see Opcode#I2S + * @see ConvertInstruction */ default CodeBuilder i2s() { return with(ConvertInstruction.of(Opcode.I2S)); } /** - * Generate an instruction to add an int + * Generates an instruction to add two {@link TypeKind#INT ints}. + * * @return this builder + * @see Opcode#IADD + * @see OperatorInstruction */ default CodeBuilder iadd() { return with(OperatorInstruction.of(Opcode.IADD)); } /** - * Generate an instruction to load a int from an array + * Generates an instruction to load from an {@link TypeKind#INT int} array. + * * @return this builder + * @see Opcode#IALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder iaload() { return arrayLoad(TypeKind.INT); } /** - * Generate an instruction to calculate boolean AND of ints + * Generates an instruction to calculate bitwise AND of {@link TypeKind#INT + * ints}, also used for {@link TypeKind#BOOLEAN boolean} AND. + * * @return this builder + * @see Opcode#IAND + * @see OperatorInstruction */ default CodeBuilder iand() { return with(OperatorInstruction.of(Opcode.IAND)); } /** - * Generate an instruction to store into an int array + * Generates an instruction to store into an {@link TypeKind#INT int} array. + * * @return this builder + * @see Opcode#IASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder iastore() { return arrayStore(TypeKind.INT); } /** - * Generate an instruction pushing int constant 0 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 0 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_0 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_0() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_0)); } /** - * Generate an instruction pushing int constant 1 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 1 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_1 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_1() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_1)); } /** - * Generate an instruction pushing int constant 2 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 2 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_2 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_2() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_2)); } /** - * Generate an instruction pushing int constant 3 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 3 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_3 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_3() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_3)); } /** - * Generate an instruction pushing int constant 4 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 4 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_4 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_4() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_4)); } /** - * Generate an instruction pushing int constant 5 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant 5 onto + * the operand stack. + * * @return this builder + * @see Opcode#ICONST_5 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_5() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_5)); } /** - * Generate an instruction pushing int constant -1 onto the operand stack + * Generates an instruction pushing {@link TypeKind#INT int} constant -1 + * onto the operand stack. + * * @return this builder + * @see Opcode#ICONST_M1 + * @see #loadConstant(int) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder iconst_m1() { return with(ConstantInstruction.ofIntrinsic(Opcode.ICONST_M1)); } /** - * Generate an instruction to divide ints + * Generates an instruction to divide {@link TypeKind#INT ints}. + * * @return this builder + * @see Opcode#IDIV + * @see OperatorInstruction */ default CodeBuilder idiv() { return with(OperatorInstruction.of(Opcode.IDIV)); } /** - * Generate an instruction to branch if reference comparison succeeds + * Generates an instruction to branch if {@link TypeKind#REFERENCE reference} + * comparison {@code operand1 == operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ACMPEQ + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_acmpeq(Label target) { return branch(Opcode.IF_ACMPEQ, target); } /** - * Generate an instruction to branch if reference comparison succeeds + * Generates an instruction to branch if {@link TypeKind#REFERENCE reference} + * comparison {@code operand1 != operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ACMPNE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_acmpne(Label target) { return branch(Opcode.IF_ACMPNE, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 == operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPEQ + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmpeq(Label target) { return branch(Opcode.IF_ICMPEQ, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 >= operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPGE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmpge(Label target) { return branch(Opcode.IF_ICMPGE, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 > operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPGT + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmpgt(Label target) { return branch(Opcode.IF_ICMPGT, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 <= operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPLE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmple(Label target) { return branch(Opcode.IF_ICMPLE, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 < operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPLT + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmplt(Label target) { return branch(Opcode.IF_ICMPLT, target); } /** - * Generate an instruction to branch if int comparison succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * {@code operand1 != operand2} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IF_ICMPNE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder if_icmpne(Label target) { return branch(Opcode.IF_ICMPNE, target); } /** - * Generate an instruction to branch if reference is not null + * Generates an instruction to branch if {@link TypeKind#REFERENCE reference} + * is not {@code null}. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFNONNULL + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifnonnull(Label target) { return branch(Opcode.IFNONNULL, target); } /** - * Generate an instruction to branch if reference is null + * Generates an instruction to branch if {@link TypeKind#REFERENCE reference} + * is {@code null}. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFNULL + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifnull(Label target) { return branch(Opcode.IFNULL, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code == 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFEQ + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifeq(Label target) { return branch(Opcode.IFEQ, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code >= 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFGE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifge(Label target) { return branch(Opcode.IFGE, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code > 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFGT + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifgt(Label target) { return branch(Opcode.IFGT, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code <= 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFLE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifle(Label target) { return branch(Opcode.IFLE, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code < 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFLT + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder iflt(Label target) { return branch(Opcode.IFLT, target); } /** - * Generate an instruction to branch if int comparison with zero succeeds + * Generates an instruction to branch if {@link TypeKind#INT int} comparison + * with zero {@code != 0} succeeds. + *

    + * This may generate multiple instructions to accomplish the same effect if + * {@link ClassFile.ShortJumpsOption#FIX_SHORT_JUMPS} is set and {@code + * target} cannot be encoded as a BCI offset in {@code [-32768, 32767]}. + * * @param target the branch target * @return this builder + * @see Opcode#IFNE + * @see #branch(Opcode, Label) + * @see BranchInstruction */ default CodeBuilder ifne(Label target) { return branch(Opcode.IFNE, target); } /** - * Generate an instruction to increment a local variable by a constant + * Generates an instruction to increment an {@link TypeKind#INT int} local + * 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}. + * * @param slot the local variable slot * @param val the increment value * @return this builder * @throws IllegalArgumentException if {@code slot} or {@code val} is out of range + * @see Opcode#IINC + * @see IncrementInstruction */ default CodeBuilder iinc(int slot, int val) { return with(IncrementInstruction.of(slot, val)); } /** - * Generate an instruction to load an int from a local variable - * - *

    This may also generate {@code iload_} and - * {@code wide iload} instructions. + * Generates an instruction to load an {@link TypeKind#INT int} from a local + * variable. + *

    + * This may also generate {@link Opcode#ILOAD_0 iload_<N>} and {@link + * Opcode#ILOAD_W wide iload} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#ILOAD + * @see #loadLocal(TypeKind, int) + * @see LoadInstruction */ default CodeBuilder iload(int slot) { return loadLocal(TypeKind.INT, slot); } /** - * Generate an instruction to multiply ints + * Generates an instruction to multiply {@link TypeKind#INT ints}. + * * @return this builder + * @see Opcode#IMUL + * @see OperatorInstruction */ default CodeBuilder imul() { return with(OperatorInstruction.of(Opcode.IMUL)); } /** - * Generate an instruction to negate an int + * Generates an instruction to negate an {@link TypeKind#INT int}. + * * @return this builder + * @see Opcode#INEG + * @see OperatorInstruction */ default CodeBuilder ineg() { return with(OperatorInstruction.of(Opcode.INEG)); } /** - * Generate an instruction to determine if an object is of the given type + * Generates an instruction to determine if an object is of the given type, + * producing a {@link TypeKind#BOOLEAN boolean} result on the operand stack. * - * @apiNote The instruction's name is {@code instanceof}, which coincides with a + * @apiNote + * The instruction's name is {@code instanceof}, which coincides with a * reserved keyword of the Java programming language, thus this method is * named with camel case instead. * * @param target the target type * @return this builder + * @see Opcode#INSTANCEOF + * @see TypeCheckInstruction */ default CodeBuilder instanceOf(ClassEntry target) { return with(TypeCheckInstruction.of(Opcode.INSTANCEOF, target)); } /** - * Generate an instruction to determine if an object is of the given type + * Generates an instruction to determine if an object is of the given type, + * producing a {@link TypeKind#BOOLEAN boolean} result on the operand stack. * - * @apiNote The instruction's name is {@code instanceof}, which coincides with a + * @apiNote + * The instruction's name is {@code instanceof}, which coincides with a * reserved keyword of the Java programming language, thus this method is * named with camel case instead. * * @param target the target type * @return this builder * @throws IllegalArgumentException if {@code target} represents a primitive type + * @see Opcode#INSTANCEOF + * @see TypeCheckInstruction */ default CodeBuilder instanceOf(ClassDesc target) { return instanceOf(constantPool().classEntry(target)); } /** - * Generate an instruction to invoke a dynamically-computed call site + * Generates an instruction to invoke a dynamically-computed call site. + * * @param ref the dynamic call site * @return this builder + * @see Opcode#INVOKEDYNAMIC + * @see InvokeDynamicInstruction */ default CodeBuilder invokedynamic(InvokeDynamicEntry ref) { return with(InvokeDynamicInstruction.of(ref)); } /** - * Generate an instruction to invoke a dynamically-computed call site + * Generates an instruction to invoke a dynamically-computed call site. + * * @param ref the dynamic call site * @return this builder + * @see Opcode#INVOKEDYNAMIC + * @see InvokeDynamicInstruction */ default CodeBuilder invokedynamic(DynamicCallSiteDesc ref) { MethodHandleEntry bsMethod = handleDescToHandleInfo(constantPool(), (DirectMethodHandleDesc) ref.bootstrapMethod()); @@ -1771,649 +2491,920 @@ default CodeBuilder invokedynamic(DynamicCallSiteDesc ref) { } /** - * Generate an instruction to invoke an interface method + * Generates an instruction to invoke an interface method. + * * @param ref the interface method reference * @return this builder + * @see Opcode#INVOKEINTERFACE + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) { return invoke(Opcode.INVOKEINTERFACE, ref); } /** - * Generate an instruction to invoke an interface method - * @param owner the owner class + * Generates an instruction to invoke an interface method. + * + * @param owner the owner interface * @param name the method name * @param type the method type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKEINTERFACE + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokeinterface(ClassDesc owner, String name, MethodTypeDesc type) { return invoke(Opcode.INVOKEINTERFACE, constantPool().interfaceMethodRefEntry(owner, name, type)); } /** - * Generate an instruction to invoke an instance method; direct invocation of instance initialization - * methods and methods of the current class and its supertypes + * Generates an instruction to invoke an instance method in an interface; + * direct invocation of methods of the current class and its supertypes. + * * @param ref the interface method reference * @return this builder + * @see Opcode#INVOKESPECIAL + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokespecial(InterfaceMethodRefEntry ref) { return invoke(Opcode.INVOKESPECIAL, ref); } /** - * Generate an instruction to invoke an instance method; direct invocation of instance initialization - * methods and methods of the current class and its supertypes + * Generates an instruction to invoke an instance method in a class; direct + * invocation of instance initialization methods and methods of the current + * class and its supertypes. + * * @param ref the method reference * @return this builder + * @see Opcode#INVOKESPECIAL + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokespecial(MethodRefEntry ref) { return invoke(Opcode.INVOKESPECIAL, ref); } /** - * Generate an instruction to invoke an instance method; direct invocation of instance initialization - * methods and methods of the current class and its supertypes - * @param owner the owner class + * Generates an instruction to invoke an instance method in a class; direct + * invocation of instance initialization methods and methods of the current + * class and its supertypes. + * + * @param owner the owner class, must not be an interface * @param name the method name * @param type the method type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKESPECIAL + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type) { return invoke(Opcode.INVOKESPECIAL, owner, name, type, false); } /** - * Generate an instruction to invoke an instance method; direct invocation of instance initialization - * methods and methods of the current class and its supertypes - * @param owner the owner class + * Generates an instruction to invoke an instance method; direct invocation + * of instance initialization methods and methods of the current class and + * its supertypes. + * + * @param owner the owner class or interface * @param name the method name * @param type the method type - * @param isInterface the interface method invocation indication + * @param isInterface whether the owner is an interface * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKESPECIAL + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type, boolean isInterface) { return invoke(Opcode.INVOKESPECIAL, owner, name, type, isInterface); } /** - * Generate an instruction to invoke a class (static) method + * Generates an instruction to invoke a class (static) method of an interface. + * * @param ref the interface method reference * @return this builder + * @see Opcode#INVOKESTATIC + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokestatic(InterfaceMethodRefEntry ref) { return invoke(Opcode.INVOKESTATIC, ref); } /** - * Generate an instruction to invoke a class (static) method + * Generates an instruction to invoke a class (static) method of a class. + * * @param ref the method reference * @return this builder + * @see Opcode#INVOKESTATIC + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokestatic(MethodRefEntry ref) { return invoke(Opcode.INVOKESTATIC, ref); } /** - * Generate an instruction to invoke a class (static) method - * @param owner the owner class + * Generates an instruction to invoke a class (static) method of a class. + * + * @param owner the owner class, must not be an interface * @param name the method name * @param type the method type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKESTATIC + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type) { return invoke(Opcode.INVOKESTATIC, owner, name, type, false); } /** - * Generate an instruction to invoke a class (static) method - * @param owner the owner class + * Generates an instruction to invoke a class (static) method. + * + * @param owner the owner class or interface * @param name the method name * @param type the method type - * @param isInterface the interface method invocation indication + * @param isInterface whether the owner is an interface * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKESTATIC + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type, boolean isInterface) { return invoke(Opcode.INVOKESTATIC, owner, name, type, isInterface); } /** - * Generate an instruction to invoke an instance method; dispatch based on class + * Generates an instruction to invoke an instance method; dispatch based on class. + * * @param ref the method reference * @return this builder + * @see Opcode#INVOKEVIRTUAL + * @see #invoke(Opcode, MemberRefEntry) + * @see InvokeInstruction */ default CodeBuilder invokevirtual(MethodRefEntry ref) { return invoke(Opcode.INVOKEVIRTUAL, ref); } /** - * Generate an instruction to invoke an instance method; dispatch based on class - * @param owner the owner class + * Generates an instruction to invoke an instance method; dispatch based on class. + * + * @param owner the owner class, must not be an interface * @param name the method name * @param type the method type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#INVOKEVIRTUAL + * @see #invoke(Opcode, ClassDesc, String, MethodTypeDesc, boolean) + * @see InvokeInstruction */ default CodeBuilder invokevirtual(ClassDesc owner, String name, MethodTypeDesc type) { return invoke(Opcode.INVOKEVIRTUAL, owner, name, type, false); } /** - * Generate an instruction to calculate boolean OR of ints + * Generates an instruction to calculate bitwise OR of {@link TypeKind#INT + * ints}, also used for {@link TypeKind#BOOLEAN boolean} OR. + * * @return this builder + * @see Opcode#IOR + * @see OperatorInstruction */ default CodeBuilder ior() { return with(OperatorInstruction.of(Opcode.IOR)); } /** - * Generate an instruction to calculate ints remainder + * Generates an instruction to calculate {@link TypeKind#INT ints} remainder. + * * @return this builder + * @see Opcode#IREM + * @see OperatorInstruction */ default CodeBuilder irem() { return with(OperatorInstruction.of(Opcode.IREM)); } /** - * Generate an instruction to return an int from the method + * Generates an instruction to return an {@link TypeKind#INT int} from this + * method. + * * @return this builder + * @see Opcode#IRETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder ireturn() { return return_(TypeKind.INT); } /** - * Generate an instruction to shift an int left + * Generates an instruction to shift an {@link TypeKind#INT int} left. + * * @return this builder + * @see Opcode#ISHL + * @see OperatorInstruction */ default CodeBuilder ishl() { return with(OperatorInstruction.of(Opcode.ISHL)); } /** - * Generate an instruction to shift an int right + * Generates an instruction to shift an {@link TypeKind#INT int} right. + * This carries the sign bit to the vacated most significant bits, as + * opposed to {@link #iushr()} that fills vacated most significant bits with + * {@code 0}. + * * @return this builder + * @see Opcode#ISHR + * @see OperatorInstruction */ default CodeBuilder ishr() { return with(OperatorInstruction.of(Opcode.ISHR)); } /** - * Generate an instruction to store an int into a local variable - * - *

    This may also generate {@code istore_} and - * {@code wide istore} instructions. + * Generates an instruction to store an {@link TypeKind#INT int} into a + * local variable. + *

    + * This may also generate {@link Opcode#ISTORE_0 istore_<N>} and + * {@link Opcode#ISTORE_W wide istore} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#ISTORE + * @see #storeLocal(TypeKind, int) + * @see StoreInstruction */ default CodeBuilder istore(int slot) { return storeLocal(TypeKind.INT, slot); } /** - * Generate an instruction to subtract ints + * Generates an instruction to subtract {@link TypeKind#INT ints}. + * * @return this builder + * @see Opcode#ISUB + * @see OperatorInstruction */ default CodeBuilder isub() { return with(OperatorInstruction.of(Opcode.ISUB)); } /** - * Generate an instruction to logical shift an int right + * Generates an instruction to logical shift an {@link TypeKind#INT int} + * right. This fills vacated most significant bits with {@code 0}, as + * opposed to {@link #ishr()} that carries the sign bit to the vacated most + * significant bits. + * * @return this builder + * @see Opcode#IUSHR + * @see OperatorInstruction */ default CodeBuilder iushr() { return with(OperatorInstruction.of(Opcode.IUSHR)); } /** - * Generate an instruction to calculate boolean XOR of ints + * Generates an instruction to calculate bitwise XOR of {@link TypeKind#INT + * ints}. This can also be used for {@link TypeKind#BOOLEAN boolean} XOR. + * * @return this builder + * @see Opcode#IXOR + * @see OperatorInstruction */ default CodeBuilder ixor() { return with(OperatorInstruction.of(Opcode.IXOR)); } /** - * Generate an instruction to access a jump table by key match and jump + * Generates an instruction to access a jump table by key match and jump. + * * @param defaultTarget the default jump target * @param cases the switch cases * @return this builder + * @see Opcode#LOOKUPSWITCH + * @see LookupSwitchInstruction */ default CodeBuilder lookupswitch(Label defaultTarget, List cases) { return with(LookupSwitchInstruction.of(defaultTarget, cases)); } /** - * Generate an instruction to convert a long into a double + * Generates an instruction to convert a {@link TypeKind#LONG long} into a + * {@link TypeKind#DOUBLE double}. + * * @return this builder + * @see Opcode#L2D + * @see OperatorInstruction */ default CodeBuilder l2d() { return with(ConvertInstruction.of(Opcode.L2D)); } /** - * Generate an instruction to convert a long into a float + * Generates an instruction to convert a {@link TypeKind#LONG long} into a + * {@link TypeKind#FLOAT float}. + * * @return this builder + * @see Opcode#L2F + * @see OperatorInstruction */ default CodeBuilder l2f() { return with(ConvertInstruction.of(Opcode.L2F)); } /** - * Generate an instruction to convert a long into an int + * Generates an instruction to convert a {@link TypeKind#LONG long} into an + * {@link TypeKind#INT int}. + * * @return this builder + * @see Opcode#L2I + * @see OperatorInstruction */ default CodeBuilder l2i() { return with(ConvertInstruction.of(Opcode.L2I)); } /** - * Generate an instruction to add a long + * Generates an instruction to add two {@link TypeKind#LONG longs}. + * * @return this builder + * @see Opcode#LADD + * @see OperatorInstruction */ default CodeBuilder ladd() { return with(OperatorInstruction.of(Opcode.LADD)); } /** - * Generate an instruction to load a long from an array + * Generates an instruction to load from a {@link TypeKind#LONG long} array. + * * @return this builder + * @see Opcode#LALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder laload() { return arrayLoad(TypeKind.LONG); } /** - * Generate an instruction to calculate boolean AND of longs + * Generates an instruction to calculate bitwise AND of {@link TypeKind#LONG + * longs}. + * * @return this builder + * @see Opcode#LAND + * @see OperatorInstruction */ default CodeBuilder land() { return with(OperatorInstruction.of(Opcode.LAND)); } /** - * Generate an instruction to store into a long array + * Generates an instruction to store into a {@link TypeKind#LONG long} array. + * * @return this builder + * @see Opcode#LASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder lastore() { return arrayStore(TypeKind.LONG); } /** - * Generate an instruction to compare longs + * Generates an instruction to compare {@link TypeKind#LONG longs}. + * * @return this builder + * @see Opcode#LCMP + * @see OperatorInstruction */ default CodeBuilder lcmp() { return with(OperatorInstruction.of(Opcode.LCMP)); } /** - * Generate an instruction pushing long constant 0 onto the operand stack + * Generates an instruction pushing {@link TypeKind#LONG long} constant 0 + * onto the operand stack. + * * @return this builder + * @see Opcode#LCONST_0 + * @see #loadConstant(long) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder lconst_0() { return with(ConstantInstruction.ofIntrinsic(Opcode.LCONST_0)); } /** - * Generate an instruction pushing long constant 1 onto the operand stack + * Generates an instruction pushing {@link TypeKind#LONG long} constant 1 + * onto the operand stack. + * * @return this builder + * @see Opcode#LCONST_1 + * @see #loadConstant(long) + * @see ConstantInstruction.IntrinsicConstantInstruction */ default CodeBuilder lconst_1() { return with(ConstantInstruction.ofIntrinsic(Opcode.LCONST_1)); } /** - * Generate an instruction pushing an item from the run-time constant pool onto the operand stack - * - *

    This may also generate {@code ldc_w} and {@code ldc2_w} instructions. + * Generates an instruction pushing an item from the run-time constant pool + * onto the operand stack. + *

    + * This may also generate {@link Opcode#LDC_W ldc_w} and {@link Opcode#LDC2_W + * ldc2_w} instructions. * - * @apiNote {@link #loadConstant(ConstantDesc) loadConstant} generates more optimal instructions - * and should be used for general constants if an {@code ldc} instruction is not strictly required. + * @apiNote + * {@link #loadConstant(ConstantDesc) loadConstant} generates more optimal + * instructions and should be used for general constants if an {@code ldc} + * instruction is not strictly required. * * @param value the constant value * @return this builder + * @see Opcode#LDC + * @see #loadConstant(ConstantDesc) + * @see ConstantInstruction.LoadConstantInstruction */ default CodeBuilder ldc(ConstantDesc value) { return ldc(BytecodeHelpers.constantEntry(constantPool(), value)); } /** - * Generate an instruction pushing an item from the run-time constant pool onto the operand stack - * - *

    This may also generate {@code ldc_w} and {@code ldc2_w} instructions. + * Generates an instruction pushing an item from the run-time constant pool + * onto the operand stack. + *

    + * This may also generate {@link Opcode#LDC_W ldc_w} and {@link Opcode#LDC2_W + * ldc2_w} instructions. * * @param entry the constant value * @return this builder + * @see Opcode#LDC + * @see #loadConstant(ConstantDesc) + * @see ConstantInstruction.LoadConstantInstruction */ default CodeBuilder ldc(LoadableConstantEntry entry) { return with(ConstantInstruction.ofLoad(BytecodeHelpers.ldcOpcode(entry), entry)); } /** - * Generate an instruction to divide longs + * Generates an instruction to divide {@link TypeKind#LONG longs}. + * * @return this builder + * @see Opcode#LDIV + * @see OperatorInstruction */ default CodeBuilder ldiv() { return with(OperatorInstruction.of(Opcode.LDIV)); } /** - * Generate an instruction to load a long from a local variable - * - *

    This may also generate {@code lload_} and - * {@code wide lload} instructions. + * Generates an instruction to load a {@link TypeKind#LONG long} from a + * local variable. + *

    + * This may also generate {@link Opcode#LLOAD_0 lload_<N>} and {@link + * Opcode#LLOAD_W wide lload} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#LLOAD + * @see #loadLocal(TypeKind, int) + * @see LoadInstruction */ default CodeBuilder lload(int slot) { return loadLocal(TypeKind.LONG, slot); } /** - * Generate an instruction to multiply longs + * Generates an instruction to multiply {@link TypeKind#LONG longs}. + * * @return this builder + * @see Opcode#LMUL + * @see OperatorInstruction */ default CodeBuilder lmul() { return with(OperatorInstruction.of(Opcode.LMUL)); } /** - * Generate an instruction to negate a long + * Generates an instruction to negate a {@link TypeKind#LONG long}. + * * @return this builder + * @see Opcode#LNEG + * @see OperatorInstruction */ default CodeBuilder lneg() { return with(OperatorInstruction.of(Opcode.LNEG)); } /** - * Generate an instruction to calculate boolean OR of longs + * Generates an instruction to calculate bitwise OR of {@link TypeKind#LONG + * longs}. + * * @return this builder + * @see Opcode#LOR + * @see OperatorInstruction */ default CodeBuilder lor() { return with(OperatorInstruction.of(Opcode.LOR)); } /** - * Generate an instruction to calculate longs remainder + * Generates an instruction to calculate {@link TypeKind#LONG longs} + * remainder. + * * @return this builder + * @see Opcode#LREM + * @see OperatorInstruction */ default CodeBuilder lrem() { return with(OperatorInstruction.of(Opcode.LREM)); } /** - * Generate an instruction to return a long from the method + * Generates an instruction to return a {@link TypeKind#LONG long} from this + * method. + * * @return this builder + * @see Opcode#LRETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder lreturn() { return return_(TypeKind.LONG); } /** - * Generate an instruction to shift a long left + * Generates an instruction to shift a {@link TypeKind#LONG long} left. + * * @return this builder + * @see Opcode#LSHL + * @see OperatorInstruction */ default CodeBuilder lshl() { return with(OperatorInstruction.of(Opcode.LSHL)); } /** - * Generate an instruction to shift a long right + * Generates an instruction to shift a {@link TypeKind#LONG long} right. + * This carries the sign bit to the vacated most significant bits, as + * opposed to {@link #lushr()} that fills vacated most significant bits with + * {@code 0}. + * * @return this builder + * @see Opcode#LSHR + * @see OperatorInstruction */ default CodeBuilder lshr() { return with(OperatorInstruction.of(Opcode.LSHR)); } /** - * Generate an instruction to store a long into a local variable - * - *

    This may also generate {@code lstore_} and - * {@code wide lstore} instructions. + * Generates an instruction to store a {@link TypeKind#LONG long} into a + * local variable. + *

    + * This may also generate {@link Opcode#LSTORE_0 lstore_<N>} and + * {@link Opcode#LSTORE_W wide lstore} instructions. * * @param slot the local variable slot * @return this builder * @throws IllegalArgumentException if {@code slot} is out of range + * @see Opcode#LSTORE + * @see #storeLocal(TypeKind, int) + * @see StoreInstruction */ default CodeBuilder lstore(int slot) { return storeLocal(TypeKind.LONG, slot); } /** - * Generate an instruction to subtract longs + * Generates an instruction to subtract {@link TypeKind#LONG longs}. + * * @return this builder + * @see Opcode#LSUB + * @see OperatorInstruction */ default CodeBuilder lsub() { return with(OperatorInstruction.of(Opcode.LSUB)); } /** - * Generate an instruction to logical shift a long left + * Generates an instruction to logical shift a {@link TypeKind#LONG long} + * right. This fills vacated most significant bits with {@code 0}, as + * opposed to {@link #lshr()} that carries the sign bit to the vacated most + * significant bits. + * * @return this builder + * @see Opcode#LUSHR + * @see OperatorInstruction */ default CodeBuilder lushr() { return with(OperatorInstruction.of(Opcode.LUSHR)); } /** - * Generate an instruction to calculate boolean XOR of longs + * Generates an instruction to calculate bitwise XOR of {@link TypeKind#LONG + * longs}. + * * @return this builder + * @see Opcode#LXOR + * @see OperatorInstruction */ default CodeBuilder lxor() { return with(OperatorInstruction.of(Opcode.LXOR)); } /** - * Generate an instruction to enter monitor for an object + * Generates an instruction to enter monitor for an object. + * * @return this builder + * @see Opcode#MONITORENTER + * @see MonitorInstruction */ default CodeBuilder monitorenter() { return with(MonitorInstruction.of(Opcode.MONITORENTER)); } /** - * Generate an instruction to exit monitor for an object + * Generates an instruction to exit monitor for an object. + * * @return this builder + * @see Opcode#MONITOREXIT + * @see MonitorInstruction */ default CodeBuilder monitorexit() { return with(MonitorInstruction.of(Opcode.MONITOREXIT)); } /** - * Generate an instruction to create a new multidimensional array + * Generates an instruction to create a new multidimensional array. + * * @param array the array type * @param dims the number of dimensions * @return this builder * @throws IllegalArgumentException if {@code dims} is out of range + * @see Opcode#MULTIANEWARRAY + * @see NewMultiArrayInstruction */ default CodeBuilder multianewarray(ClassEntry array, int dims) { return with(NewMultiArrayInstruction.of(array, dims)); } /** - * Generate an instruction to create a new multidimensional array + * Generates an instruction to create a new multidimensional array. + * * @param array the array type * @param dims the number of dimensions * @return this builder * @throws IllegalArgumentException if {@code array} represents a primitive type - * or if {@code dims} is out of range + * or if {@code dims} is out of range + * @see Opcode#MULTIANEWARRAY + * @see NewMultiArrayInstruction */ default CodeBuilder multianewarray(ClassDesc array, int dims) { return multianewarray(constantPool().classEntry(array), dims); } /** - * Generate an instruction to create a new object + * Generates an instruction to create a new object. * - * @apiNote The instruction's name is {@code new}, which coincides with a - * reserved keyword of the Java programming language, thus this method is - * named with an extra {@code _} suffix instead. + * @apiNote + * The instruction's name is {@code new}, which coincides with a reserved + * keyword of the Java programming language, thus this method is named with + * an extra {@code _} suffix instead. * * @param clazz the new class type * @return this builder + * @see Opcode#NEW + * @see NewObjectInstruction */ default CodeBuilder new_(ClassEntry clazz) { return with(NewObjectInstruction.of(clazz)); } /** - * Generate an instruction to create a new object + * Generates an instruction to create a new object. * - * @apiNote The instruction's name is {@code new}, which coincides with a - * reserved keyword of the Java programming language, thus this method is - * named with an extra {@code _} suffix instead. + * @apiNote + * The instruction's name is {@code new}, which coincides with a reserved + * keyword of the Java programming language, thus this method is named with + * an extra {@code _} suffix instead. * * @param clazz the new class type * @return this builder * @throws IllegalArgumentException if {@code clazz} represents a primitive type + * @see Opcode#NEW + * @see NewObjectInstruction */ default CodeBuilder new_(ClassDesc clazz) { return new_(constantPool().classEntry(clazz)); } /** - * Generate an instruction to create a new array of a primitive type + * Generates an instruction to create a new array of a primitive type. + * * @param typeKind the primitive array type * @return this builder * @throws IllegalArgumentException when the {@code typeKind} is not a legal * primitive array component type + * @see Opcode#NEWARRAY + * @see NewPrimitiveArrayInstruction */ default CodeBuilder newarray(TypeKind typeKind) { return with(NewPrimitiveArrayInstruction.of(typeKind)); } /** - * Generate an instruction to pop the top operand stack value + * Generates an instruction to pop the top operand stack value. + * * @return this builder + * @see Opcode#POP + * @see StackInstruction */ default CodeBuilder pop() { return with(StackInstruction.of(Opcode.POP)); } /** - * Generate an instruction to pop the top one or two operand stack values + * Generates an instruction to pop the top one or two operand stack values. + * * @return this builder + * @see Opcode#POP2 + * @see StackInstruction */ default CodeBuilder pop2() { return with(StackInstruction.of(Opcode.POP2)); } /** - * Generate an instruction to set field in an object + * Generates an instruction to set field in an object. + * * @param ref the field reference * @return this builder + * @see Opcode#PUTFIELD + * @see #fieldAccess(Opcode, FieldRefEntry) + * @see FieldInstruction */ default CodeBuilder putfield(FieldRefEntry ref) { return fieldAccess(Opcode.PUTFIELD, ref); } /** - * Generate an instruction to set field in an object + * Generates an instruction to set field in an object. + * * @param owner the owner class * @param name the field name * @param type the field type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#PUTFIELD + * @see #fieldAccess(Opcode, ClassDesc, String, ClassDesc) + * @see FieldInstruction */ default CodeBuilder putfield(ClassDesc owner, String name, ClassDesc type) { return fieldAccess(Opcode.PUTFIELD, owner, name, type); } /** - * Generate an instruction to set static field in a class + * Generates an instruction to set static field in a class. + * * @param ref the field reference * @return this builder + * @see Opcode#PUTSTATIC + * @see #fieldAccess(Opcode, FieldRefEntry) + * @see FieldInstruction */ default CodeBuilder putstatic(FieldRefEntry ref) { return fieldAccess(Opcode.PUTSTATIC, ref); } /** - * Generate an instruction to set static field in a class - * @param owner the owner class + * Generates an instruction to set static field in a class. + * + * @param owner the owner class or interface * @param name the field name * @param type the field type * @return this builder * @throws IllegalArgumentException if {@code owner} represents a primitive type + * @see Opcode#PUTSTATIC + * @see #fieldAccess(Opcode, ClassDesc, String, ClassDesc) + * @see FieldInstruction */ default CodeBuilder putstatic(ClassDesc owner, String name, ClassDesc type) { return fieldAccess(Opcode.PUTSTATIC, owner, name, type); } /** - * Generate an instruction to return void from the method + * Generates an instruction to return {@link TypeKind#VOID void} from this + * method. * - * @apiNote The instruction's name is {@code return}, which coincides with a - * reserved keyword of the Java programming language, thus this method is - * named with an extra {@code _} suffix instead. + * @apiNote + * The instruction's name is {@code return}, which coincides with a reserved + * keyword of the Java programming language, thus this method is named with + * an extra {@code _} suffix instead. * * @return this builder + * @see Opcode#RETURN + * @see #return_(TypeKind) + * @see ReturnInstruction */ default CodeBuilder return_() { return return_(TypeKind.VOID); } /** - * Generate an instruction to load a short from an array + * Generates an instruction to load from a {@link TypeKind#SHORT short} + * array. + * * @return this builder + * @see Opcode#SALOAD + * @see #arrayLoad(TypeKind) + * @see ArrayLoadInstruction */ default CodeBuilder saload() { return arrayLoad(TypeKind.SHORT); } /** - * Generate an instruction to store into a short array + * Generates an instruction to store into a {@link TypeKind#SHORT short} + * array. + * * @return this builder + * @see Opcode#SASTORE + * @see #arrayStore(TypeKind) + * @see ArrayStoreInstruction */ default CodeBuilder sastore() { return arrayStore(TypeKind.SHORT); } /** - * Generate an instruction pushing an int in the range of short onto the operand stack. + * Generates an instruction pushing an {@link TypeKind#INT int} in the range + * of {@link TypeKind#SHORT short}, {@code [-32768, 32767]}, onto the + * operand stack. + * * @param s the int in the range of short * @return this builder * @throws IllegalArgumentException if {@code s} is out of range of short + * @see Opcode#SIPUSH + * @see #loadConstant(int) + * @see ConstantInstruction.ArgumentConstantInstruction */ default CodeBuilder sipush(int s) { return with(ConstantInstruction.ofArgument(Opcode.SIPUSH, s)); } /** - * Generate an instruction to swap the top two operand stack values + * Generates an instruction to swap the top two operand stack values. + * * @return this builder + * @see Opcode#SWAP + * @see StackInstruction */ default CodeBuilder swap() { return with(StackInstruction.of(Opcode.SWAP)); } /** - * Generate an instruction to access a jump table by index and jump - * @param low the low key value - * @param high the high key value + * Generates an instruction to access a jump table by index and jump. + * + * @param low the minimum key, inclusive + * @param high the maximum key, inclusive * @param defaultTarget the default jump target * @param cases the switch cases * @return this builder + * @see Opcode#TABLESWITCH + * @see TableSwitchInstruction */ default CodeBuilder tableswitch(int low, int high, Label defaultTarget, List cases) { return with(TableSwitchInstruction.of(low, high, defaultTarget, cases)); } /** - * Generate an instruction to access a jump table by index and jump + * Generates an instruction to access a jump table by index and jump. + * Computes the minimum and maximum keys from the {@code cases}. + * * @param defaultTarget the default jump target * @param cases the switch cases * @return this builder + * @see Opcode#TABLESWITCH + * @see #tableswitch(int, int, Label, List) + * @see TableSwitchInstruction */ default CodeBuilder tableswitch(Label defaultTarget, List cases) { int low = Integer.MAX_VALUE; diff --git a/src/java.base/share/classes/java/lang/classfile/CodeElement.java b/src/java.base/share/classes/java/lang/classfile/CodeElement.java index 63669d41014bd..9ca5138334403 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeElement.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeElement.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 @@ -29,13 +29,20 @@ import java.lang.classfile.attribute.StackMapTableAttribute; /** - * A marker interface for elements that can appear when traversing - * a {@link CodeModel} or be presented to a {@link CodeBuilder}. Code elements - * are either an {@link Instruction}, which models an instruction in the body - * of a method, or a {@link PseudoInstruction}, which models metadata from - * the code attribute, such as line number metadata, local variable metadata, - * exception metadata, label target metadata, etc. + * Marker interface for a member element of a {@link CodeModel}. Such an + * element can appear when traversing a {@link CodeModel} unless otherwise + * specified, be supplied to a {@link CodeBuilder}, and be processed by a + * {@link CodeTransform}. + *

    + * Code elements can be categorized into {@link Instruction}, {@link + * PseudoInstruction}, and {@link Attribute}. Unlike in other {@link + * CompoundElement}, the order of elements for all {@link Instruction}s and some + * {@link PseudoInstruction}s is significant. * + * @see ClassFileElement##membership Membership Elements + * @see ClassElement + * @see MethodElement + * @see FieldElement * @sealedGraph * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/CodeModel.java b/src/java.base/share/classes/java/lang/classfile/CodeModel.java index 644f766056418..614b4cfda84cd 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeModel.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeModel.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 @@ -25,17 +25,44 @@ package java.lang.classfile; +import java.lang.classfile.ClassFile.DeadLabelsOption; +import java.lang.classfile.ClassFile.DebugElementsOption; +import java.lang.classfile.ClassFile.LineNumbersOption; +import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.attribute.CodeAttribute; +import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.instruction.ExceptionCatch; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import jdk.internal.classfile.impl.BufferedCodeBuilder; /** - * Models the body of a method (the {@code Code} attribute). The instructions - * of the method body are accessed via a streaming view. + * Models the body of a method (the {@code Code} attribute). A {@code Code} + * attribute is viewed as a {@linkplain CompoundElement composition} of {@link + * CodeElement}s, which is the only way to access {@link Instruction}s; the + * order of elements of a code model is significant. + *

    + * A {@code CodeModel} is obtained from {@link MethodModel#code()}, or in the + * traversal of the member elements of a method. + *

    + * {@link MethodBuilder#withCode} is the main way to build code models. {@link + * MethodBuilder#transformCode} and {@link CodeBuilder#transforming} allow + * creating new {@code Code} attributes by selectively processing the original + * code elements and directing the results to a code builder. + *

    + * A {@code Code} attribute holds attributes, but they are usually not member + * elements, but are decomposed to {@link PseudoInstruction}, accessible + * according to {@link DeadLabelsOption}, {@link DebugElementsOption}, and + * {@link LineNumbersOption}. {@link StackMapTableAttribute} can only be + * accessed via {@linkplain AttributedElement explicit attribute reading}, as it + * is considered a derived property from the code body. * + * @see MethodModel#code() + * @see CodeTransform + * @see CodeAttribute + * @jvms 4.7.3 The {@code Code} Attribute * @since 24 */ public sealed interface CodeModel diff --git a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java b/src/java.base/share/classes/java/lang/classfile/CodeTransform.java index b76c02bf5fb53..404656308531d 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeTransform.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 @@ -32,10 +32,22 @@ import static java.util.Objects.requireNonNull; /** - * A transformation on streams of {@link CodeElement}. - * - * @see ClassFileTransform + * A transformation on streams of {@link CodeElement}. The stream can come + * from a {@link CodeModel}, or a handler to a {@link CodeBuilder} as in + * {@link CodeBuilder#transforming}. + *

    + * Refer to {@link ClassFileTransform} for general guidance and caution around + * the use of transforms for structures in the {@code class} file format. + *

    + * A code transform can be lifted to a method or a class transform via {@link + * MethodTransform#transformingCode(CodeTransform)} and {@link + * ClassTransform#transformingMethodBodies(CodeTransform)}, transforming only + * the {@link CodeModel} within those structures and passing all other elements + * to the builders. * + * @see CodeModel + * @see MethodBuilder#transformCode + * @see CodeBuilder#transforming * @since 24 */ @FunctionalInterface @@ -43,7 +55,7 @@ public non-sealed interface CodeTransform extends ClassFileTransform { /** - * A code transform that sends all elements to the builder. + * A code transform that passes all elements to the builder. */ CodeTransform ACCEPT_ALL = new CodeTransform() { @Override @@ -53,7 +65,7 @@ public void accept(CodeBuilder builder, CodeElement element) { }; /** - * Create a stateful code transform from a {@link Supplier}. The supplier + * Creates a stateful code transform from a {@link Supplier}. The supplier * will be invoked for each transformation. * * @param supplier a {@link Supplier} that produces a fresh transform object @@ -65,7 +77,7 @@ static CodeTransform ofStateful(Supplier supplier) { } /** - * Create a code transform that passes each element through to the builder, + * Creates a code transform that passes each element through to the builder, * and calls the specified function when transformation is complete. * * @param finisher the function to call when transformation is complete diff --git a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java b/src/java.base/share/classes/java/lang/classfile/CompoundElement.java index d9f9fe1e5f939..1b06a26030206 100644 --- a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java +++ b/src/java.base/share/classes/java/lang/classfile/CompoundElement.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 @@ -37,14 +37,26 @@ import jdk.internal.classfile.components.ClassPrinter; /** - * A {@link ClassFileElement} that has complex structure defined in terms of - * other classfile elements, such as a method, field, method body, or entire - * class. When encountering a {@linkplain CompoundElement}, clients have the - * option to treat the element as a single entity (e.g., an entire method) - * or to traverse the contents of that element with the methods in this class - * (e.g., {@link #forEach(Consumer)}, etc.) - * @param the element type + * A {@code class} file structure that can be viewed as a composition of its + * member structures. {@code CompoundElement} allows users to traverse these + * member elements with {@link #forEach(Consumer)} or {@link #elementStream()}, + * or buffer the elements obtained from the traversal through {@link + * #iterator()} or {@link #elementList()}. + *

    + * Unless otherwise specified, all member elements of compatible type will be + * presented during the traversal if they exist in this element. Some member + * elements specify that they may appear at most once in this element; if such + * elements are presented multiple times, the latest occurrence is authentic and + * all previous occurrences should be ignored. + *

    + * {@code CompoundElement}s can be constructed by {@link ClassFileBuilder}s. + * {@link ClassFileBuilder#transform(CompoundElement, ClassFileTransform)} + * provides an easy way to create a new structure by selectively processing + * the original member structures and directing the results to the builder. * + * @param the member element type + * @see ClassFileElement##membership Membership Elements + * @see ClassFileBuilder * @sealedGraph * @since 24 */ @@ -52,15 +64,16 @@ public sealed interface CompoundElement extends ClassFileElement, Iterable permits ClassModel, CodeModel, FieldModel, MethodModel, jdk.internal.classfile.impl.AbstractUnboundModel { /** - * Invoke the provided handler with each element contained in this - * compound element + * Invokes the provided handler with each member element in this compound + * element. + * * @param consumer the handler */ @Override void forEach(Consumer consumer); /** - * {@return an {@link Iterator} describing all the elements contained in this + * {@return an {@link Iterator} describing all member elements in this * compound element} */ @Override @@ -69,8 +82,8 @@ default Iterator iterator() { } /** - * {@return a {@link Stream} containing all the elements contained in this - * compound element} + * {@return a {@link Stream} containing all member elements in this compound + * element} */ default Stream elementStream() { return StreamSupport.stream(Spliterators.spliteratorUnknownSize( @@ -80,8 +93,8 @@ default Stream elementStream() { } /** - * {@return an {@link List} containing all the elements contained in this - * compound element} + * {@return a {@link List} containing all member elements in this compound + * element} */ default List elementList() { List list = new ArrayList<>(); @@ -95,9 +108,11 @@ public void accept(E e) { } /** - * {@return a text representation of the compound element and its contents for debugging purposes} + * {@return a text representation of the compound element and its contents + * for debugging purposes} * - * The format, structure and exact contents of the returned string are not specified and may change at any time in the future. + * The format, structure and exact contents of the returned string are not + * specified and may change at any time in the future. */ default String toDebugString() { StringBuilder text = new StringBuilder(); diff --git a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java index c473e09cab722..477aa6984a238 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.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 @@ -25,7 +25,7 @@ package java.lang.classfile; -import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.constant.ClassDesc; import java.lang.reflect.AccessFlag; import java.util.function.Consumer; @@ -34,14 +34,17 @@ import jdk.internal.classfile.impl.TerminalFieldBuilder; /** - * A builder for fields. Builders are not created directly; they are passed - * to handlers by methods such as {@link ClassBuilder#withField(Utf8Entry, Utf8Entry, Consumer)} - * or to field transforms. The elements of a field can be specified - * abstractly (by passing a {@link FieldElement} to {@link #with(ClassFileElement)} - * or concretely by calling the various {@code withXxx} methods. + * A builder for fields. The main way to obtain a field builder is via {@link + * ClassBuilder#withField(String, ClassDesc, Consumer)}. The {@linkplain + * ClassBuilder#withField(String, ClassDesc, int) access flag overload} is + * useful if no attribute needs to be configured, skipping the handler. + *

    + * Refer to {@link ClassFileBuilder} for general guidance and caution around + * the use of builders for structures in the {@code class} file format. * + * @see ClassBuilder#withField(String, ClassDesc, Consumer) + * @see FieldModel * @see FieldTransform - * * @since 24 */ public sealed interface FieldBuilder @@ -50,8 +53,12 @@ public sealed interface FieldBuilder /** * Sets the field access flags. + * * @param flags the access flags, as a bit mask * @return this builder + * @see AccessFlags + * @see AccessFlag.Location#FIELD + * @see ClassBuilder#withField(String, ClassDesc, int) */ default FieldBuilder withFlags(int flags) { return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); @@ -59,8 +66,12 @@ default FieldBuilder withFlags(int flags) { /** * Sets the field access flags. + * * @param flags the access flags, as a bit mask * @return this builder + * @see AccessFlags + * @see AccessFlag.Location#FIELD + * @see ClassBuilder#withField(String, ClassDesc, int) */ default FieldBuilder withFlags(AccessFlag... flags) { return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); diff --git a/src/java.base/share/classes/java/lang/classfile/FieldElement.java b/src/java.base/share/classes/java/lang/classfile/FieldElement.java index a2c1b22751efe..a1c9905b6ddb3 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldElement.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldElement.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 @@ -27,9 +27,18 @@ import java.lang.classfile.attribute.*; /** - * A marker interface for elements that can appear when traversing - * a {@link FieldModel} or be presented to a {@link FieldBuilder}. + * Marker interface for a member element of a {@link FieldModel}. Such an + * element can appear when traversing a {@link FieldModel} unless otherwise + * specified, be supplied to a {@link FieldBuilder}, and be processed by a + * {@link FieldTransform}. + *

    + * {@link AccessFlags} is the only member element of a field that appear exactly + * once during the traversal of a {@link FieldModel}. * + * @see ClassFileElement##membership Membership Elements + * @see ClassElement + * @see MethodElement + * @see CodeElement * @sealedGraph * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/FieldModel.java b/src/java.base/share/classes/java/lang/classfile/FieldModel.java index 89fa1e192b04f..21c3d903b68cd 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldModel.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldModel.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 @@ -27,24 +27,43 @@ import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.ClassDesc; +import java.lang.reflect.AccessFlag; import java.util.Optional; +import java.util.function.Consumer; import jdk.internal.classfile.impl.BufferedFieldBuilder; import jdk.internal.classfile.impl.FieldImpl; import jdk.internal.classfile.impl.Util; /** - * Models a field. The contents of the field can be traversed via - * a streaming view, or via random access (e.g., - * {@link #flags()}), or by freely mixing the two. + * Models a field. A field can be viewed as a {@linkplain CompoundElement + * composition} of {@link FieldElement}s, or by random access via accessor + * methods if only specific parts of the field is needed. + *

    + * Fields can be obtained from {@link ClassModel#fields()}, or in the traversal + * of member elements of a class. + *

    + * {@link ClassBuilder#withField(String, ClassDesc, Consumer)} is the main way + * to construct fields. {@link ClassBuilder#transformField} allows creating a + * new field by selectively processing the original field elements and directing + * the results to a field builder. + *

    + * All field attributes are accessible as member elements. * + * @see ClassModel#fields() + * @see FieldTransform + * @jvms 4.5 Fields * @since 24 */ public sealed interface FieldModel extends CompoundElement, AttributedElement, ClassElement permits BufferedFieldBuilder.Model, FieldImpl { - /** {@return the access flags} */ + /** + * {@return the access flags} + * + * @see AccessFlag.Location#FIELD + */ AccessFlags flags(); /** {@return the class model this field is a member of, if known} */ @@ -53,10 +72,10 @@ public sealed interface FieldModel /** {@return the name of this field} */ Utf8Entry fieldName(); - /** {@return the field descriptor of this field} */ + /** {@return the field descriptor string of this field} */ Utf8Entry fieldType(); - /** {@return the field descriptor of this field, as a symbolic descriptor} */ + /** {@return the field type, as a symbolic descriptor} */ default ClassDesc fieldTypeSymbol() { return Util.fieldTypeSymbol(fieldType()); } diff --git a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java b/src/java.base/share/classes/java/lang/classfile/FieldTransform.java index 90313ae48f061..ef55861a328e1 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldTransform.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 @@ -34,9 +34,17 @@ /** * A transformation on streams of {@link FieldElement}. + *

    + * Refer to {@link ClassFileTransform} for general guidance and caution around + * the use of transforms for structures in the {@code class} file format. + *

    + * A field transform can be lifted to a class transform via {@link + * ClassTransform#transformingFields(FieldTransform)}, transforming only + * the {@link FieldModel} among the class members and passing all other elements + * to the builders. * - * @see ClassFileTransform - * + * @see FieldModel + * @see ClassBuilder#transformField * @since 24 */ @FunctionalInterface @@ -44,7 +52,7 @@ public non-sealed interface FieldTransform extends ClassFileTransform { /** - * A field transform that sends all elements to the builder. + * A field transform that passes all elements to the builder. */ FieldTransform ACCEPT_ALL = new FieldTransform() { @Override @@ -54,7 +62,7 @@ public void accept(FieldBuilder builder, FieldElement element) { }; /** - * Create a stateful field transform from a {@link Supplier}. The supplier + * Creates a stateful field transform from a {@link Supplier}. The supplier * will be invoked for each transformation. * * @param supplier a {@link Supplier} that produces a fresh transform object @@ -66,7 +74,7 @@ static FieldTransform ofStateful(Supplier supplier) { } /** - * Create a field transform that passes each element through to the builder, + * Creates a field transform that passes each element through to the builder, * and calls the specified function when transformation is complete. * * @param finisher the function to call when transformation is complete @@ -88,7 +96,7 @@ public void atEnd(FieldBuilder builder) { } /** - * Create a field transform that passes each element through to the builder, + * Creates a field transform that passes each element through to the builder, * except for those that the supplied {@link Predicate} is true for. * * @param filter the predicate that determines which elements to drop diff --git a/src/java.base/share/classes/java/lang/classfile/Instruction.java b/src/java.base/share/classes/java/lang/classfile/Instruction.java index 199aa6447a11a..12bf513e96aa6 100644 --- a/src/java.base/share/classes/java/lang/classfile/Instruction.java +++ b/src/java.base/share/classes/java/lang/classfile/Instruction.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 @@ -32,12 +32,15 @@ /** * Models an executable instruction in the {@code code} array of the {@link - * CodeAttribute Code} attribute of a method. + * CodeAttribute Code} attribute of a method. The order of instructions in + * a {@link CodeModel} is significant. *

    * The {@link #opcode() opcode} identifies the operation of an instruction. * Each {@linkplain Opcode#kind() kind} of opcode has its own modeling interface * for instructions. * + * @see Opcode + * @jvms 6.5 Instructions * @sealedGraph * @since 24 */ 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 2c0e5b2e54b9c..bd89c494f24c2 100644 --- a/src/java.base/share/classes/java/lang/classfile/Interfaces.java +++ b/src/java.base/share/classes/java/lang/classfile/Interfaces.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 @@ -33,16 +33,22 @@ import jdk.internal.classfile.impl.Util; /** - * Models the interfaces of a class. Delivered as a {@link - * java.lang.classfile.ClassElement} when traversing a {@link ClassModel}. + * Models the interfaces (JVMS {@jvms 4.1}) of a class. An {@code Interfaces} + * appears at most once in a {@link ClassModel}: if it does not appear, the + * class has no interfaces, which is equivalent to an {@code Interfaces} whose + * {@link #interfaces()} returns an empty list. A {@link ClassBuilder} sets + * the interfaces to an empty list if the interfaces is not supplied. * + * @see ClassModel#interfaces() + * @see ClassBuilder#withInterfaces + * @jvms 4.1 The {@code ClassFile} Structure * @since 24 */ public sealed interface Interfaces extends ClassElement permits InterfacesImpl { - /** {@return the interfaces of this class} */ + /** {@return the interfaces of this class, may be empty} */ List interfaces(); /** @@ -64,6 +70,7 @@ static Interfaces of(ClassEntry... interfaces) { /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces + * @throws IllegalArgumentException if any of {@code interfaces} is primitive */ static Interfaces ofSymbols(List interfaces) { return of(Util.entryList(interfaces)); @@ -72,6 +79,7 @@ static Interfaces ofSymbols(List interfaces) { /** * {@return an {@linkplain Interfaces} element} * @param interfaces the interfaces + * @throws IllegalArgumentException if any of {@code interfaces} is primitive */ static Interfaces ofSymbols(ClassDesc... interfaces) { return ofSymbols(Arrays.asList(interfaces)); diff --git a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java index 747cbe2e10770..ff777246fdeb1 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.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 @@ -25,7 +25,7 @@ package java.lang.classfile; -import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.constant.MethodTypeDesc; import java.lang.reflect.AccessFlag; import java.util.function.Consumer; @@ -34,14 +34,18 @@ import jdk.internal.classfile.impl.TerminalMethodBuilder; /** - * A builder for methods. Builders are not created directly; they are passed - * to handlers by methods such as {@link ClassBuilder#withMethod(Utf8Entry, Utf8Entry, int, Consumer)} - * or to method transforms. The elements of a method can be specified - * abstractly (by passing a {@link MethodElement} to {@link #with(ClassFileElement)} - * or concretely by calling the various {@code withXxx} methods. + * A builder for methods. The main way to obtain a method builder is via {@link + * ClassBuilder#withMethod(String, MethodTypeDesc, int, Consumer)}. {@link + * ClassBuilder#withMethodBody(String, MethodTypeDesc, int, Consumer)} is + * useful if no attribute on the method except {@link CodeModel Code} needs to + * be configured, skipping the method handler. + *

    + * Refer to {@link ClassFileBuilder} for general guidance and caution around + * the use of builders for structures in the {@code class} file format. * + * @see MethodModel * @see MethodTransform - * + * @jvms 4.6 Methods * @since 24 */ public sealed interface MethodBuilder @@ -49,18 +53,30 @@ public sealed interface MethodBuilder permits ChainedMethodBuilder, TerminalMethodBuilder { /** - * Sets the method access flags. + * Sets the method access flags. The {@link AccessFlag#STATIC} flag cannot + * be modified after the builder is created. + * * @param flags the access flags, as a bit mask * @return this builder + * @throws IllegalArgumentException if the {@link ClassFile#ACC_STATIC + * ACC_STATIC} flag is modified + * @see AccessFlags + * @see AccessFlag.Location#METHOD */ default MethodBuilder withFlags(int flags) { return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); } /** - * Sets the method access flags. + * Sets the method access flags. The {@link AccessFlag#STATIC} flag cannot + * be modified after the builder is created. + * * @param flags the access flags, as a bit mask * @return this builder + * @throws IllegalArgumentException if the {@link ClassFile#ACC_STATIC + * ACC_STATIC} flag is modified + * @see AccessFlags + * @see AccessFlag.Location#METHOD */ default MethodBuilder withFlags(AccessFlag... flags) { return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); @@ -68,24 +84,26 @@ default MethodBuilder withFlags(AccessFlag... flags) { /** * Build the method body for this method. + * * @param code a handler receiving a {@link CodeBuilder} * @return this builder + * @see CodeModel */ MethodBuilder withCode(Consumer code); /** * Build the method body for this method by transforming the body of another * method. - * - * @implNote - *

    This method behaves as if: + *

    + * This method behaves as if: * {@snippet lang=java : - * withCode(b -> b.transformCode(code, transform)); + * withCode(cob -> cob.transform(code, transform)); * } * * @param code the method body to be transformed * @param transform the transform to apply to the method body * @return this builder + * @see CodeTransform */ MethodBuilder transformCode(CodeModel code, CodeTransform transform); } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodElement.java b/src/java.base/share/classes/java/lang/classfile/MethodElement.java index 77254a6a82c43..8905f1fbcc3ab 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodElement.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodElement.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 @@ -27,9 +27,18 @@ import java.lang.classfile.attribute.*; /** - * A marker interface for elements that can appear when traversing - * a {@link MethodModel} or be presented to a {@link MethodBuilder}. + * Marker interface for a member element of a {@link MethodModel}. Such an + * element can appear when traversing a {@link MethodModel} unless otherwise + * specified, be supplied to a {@link MethodBuilder}, and be processed by a + * {@link MethodTransform}. + *

    + * {@link AccessFlags} is the only member element of a method that appear + * exactly once during the traversal of a {@link MethodModel}. * + * @see ClassFileElement##membership Membership Elements + * @see ClassElement + * @see FieldElement + * @see CodeElement * @sealedGraph * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/MethodModel.java b/src/java.base/share/classes/java/lang/classfile/MethodModel.java index d88051a5eb3f6..758223debe5c7 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodModel.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodModel.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 @@ -27,24 +27,43 @@ import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; import java.util.Optional; +import java.util.function.Consumer; import jdk.internal.classfile.impl.BufferedMethodBuilder; import jdk.internal.classfile.impl.MethodImpl; import jdk.internal.classfile.impl.Util; /** - * Models a method. The contents of the method can be traversed via - * a streaming view, or via random access (e.g., - * {@link #flags()}), or by freely mixing the two. + * Models a method. A method can be viewed as a {@linkplain CompoundElement + * composition} of {@link MethodElement}s, or by random access via accessor + * methods if only specific parts of the method is needed. + *

    + * Methods can be obtained from {@link ClassModel#methods()}, or in the + * traversal of member elements of a class. + *

    + * {@link ClassBuilder#withMethod(String, MethodTypeDesc, int, Consumer)} is the + * main way to construct methods. {@link ClassBuilder#transformMethod} allows + * creating a new method by selectively processing the original method elements + * and directing the results to a method builder. + *

    + * All method attributes are accessible as member elements. * + * @see ClassModel#methods() + * @see MethodTransform + * @jvms 4.6 Methods * @since 24 */ public sealed interface MethodModel extends CompoundElement, AttributedElement, ClassElement permits BufferedMethodBuilder.Model, MethodImpl { - /** {@return the access flags} */ + /** + * {@return the access flags} + * + * @see AccessFlag.Location#METHOD + */ AccessFlags flags(); /** {@return the class model this method is a member of, if known} */ @@ -53,10 +72,10 @@ public sealed interface MethodModel /** {@return the name of this method} */ Utf8Entry methodName(); - /** {@return the method descriptor of this method} */ + /** {@return the method descriptor string of this method} */ Utf8Entry methodType(); - /** {@return the method descriptor of this method, as a symbolic descriptor} */ + /** {@return the method type, as a symbolic descriptor} */ default MethodTypeDesc methodTypeSymbol() { return Util.methodTypeSymbol(methodType()); } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java b/src/java.base/share/classes/java/lang/classfile/MethodTransform.java index 865fadbae873b..d7d9a8445aa55 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodTransform.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 @@ -34,9 +34,17 @@ /** * A transformation on streams of {@link MethodElement}. + *

    + * Refer to {@link ClassFileTransform} for general guidance and caution around + * the use of transforms for structures in the {@code class} file format. + *

    + * A method transform can be lifted to a class transform via {@link + * ClassTransform#transformingMethods(MethodTransform)}, transforming only + * the {@link MethodModel} among the class members and passing all other + * elements to the builders. * - * @see ClassFileTransform - * + * @see MethodModel + * @see ClassBuilder#transformMethod * @since 24 */ @FunctionalInterface @@ -44,7 +52,7 @@ public non-sealed interface MethodTransform extends ClassFileTransform { /** - * A method transform that sends all elements to the builder. + * A method transform that passes all elements to the builder. */ MethodTransform ACCEPT_ALL = new MethodTransform() { @Override @@ -54,7 +62,7 @@ public void accept(MethodBuilder builder, MethodElement element) { }; /** - * Create a stateful method transform from a {@link Supplier}. The supplier + * Creates a stateful method transform from a {@link Supplier}. The supplier * will be invoked for each transformation. * * @param supplier a {@link Supplier} that produces a fresh transform object @@ -67,7 +75,7 @@ static MethodTransform ofStateful(Supplier supplier) { } /** - * Create a method transform that passes each element through to the builder, + * Creates a method transform that passes each element through to the builder, * and calls the specified function when transformation is complete. * * @param finisher the function to call when transformation is complete @@ -89,7 +97,7 @@ public void atEnd(MethodBuilder builder) { } /** - * Create a method transform that passes each element through to the builder, + * Creates a method transform that passes each element through to the builder, * except for those that the supplied {@link Predicate} is true for. * * @param filter the predicate that determines which elements to drop @@ -104,8 +112,9 @@ static MethodTransform dropping(Predicate filter) { } /** - * Create a method transform that transforms {@link CodeModel} elements - * with the supplied code transform. + * Creates a method transform that transforms {@link CodeModel} elements + * with the supplied code transform, passing every other element through to + * the builder. * * @param xform the method transform * @return the class transform diff --git a/src/java.base/share/classes/java/lang/classfile/Opcode.java b/src/java.base/share/classes/java/lang/classfile/Opcode.java index a3b0a37076336..99ea4696f60eb 100644 --- a/src/java.base/share/classes/java/lang/classfile/Opcode.java +++ b/src/java.base/share/classes/java/lang/classfile/Opcode.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 @@ -42,7 +42,7 @@ * instead of wide pseudo-opcodes. * * @see Instruction - * + * @jvms 6.5 Instructions * @since 24 */ public enum Opcode { @@ -1143,7 +1143,7 @@ public enum Opcode { LXOR(RawBytecodeHelper.LXOR, 1, Kind.OPERATOR), /** - * Increment local variable by constant. + * Increment {@link TypeKind#INT int} local variable by constant. * * @jvms 6.5.iinc iinc * @see Kind#INCREMENT diff --git a/src/java.base/share/classes/java/lang/classfile/PseudoInstruction.java b/src/java.base/share/classes/java/lang/classfile/PseudoInstruction.java index 456bce04c8021..6d2e76b5cb853 100644 --- a/src/java.base/share/classes/java/lang/classfile/PseudoInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/PseudoInstruction.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,13 +35,16 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction; /** - * Models metadata about a {@link CodeAttribute}, such as entries in the - * exception table, line number table, local variable table, or the mapping - * between instructions and labels. Pseudo-instructions are delivered as part - * of the element stream of a {@link CodeModel}. Delivery of some - * pseudo-instructions can be disabled by modifying the value of classfile - * options (e.g., {@link ClassFile.DebugElementsOption}). + * Models metadata about a {@link CodeModel}, derived from the {@link + * CodeAttribute Code} attribute itself or its attributes. + *

    + * Order is significant for some pseudo-instructions relative to {@link + * Instruction}s, such as {@link LabelTarget} or {@link LineNumber}. Some + * pseudo-instructions can be omitted in reading and writing according to + * certain {@link ClassFile.Option}s. These are specified in the corresponding + * modeling interfaces. * + * @sealedGraph * @since 24 */ public sealed interface PseudoInstruction diff --git a/src/java.base/share/classes/java/lang/classfile/Superclass.java b/src/java.base/share/classes/java/lang/classfile/Superclass.java index fe30fe8a8e3f7..34d11dc605f6e 100644 --- a/src/java.base/share/classes/java/lang/classfile/Superclass.java +++ b/src/java.base/share/classes/java/lang/classfile/Superclass.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 @@ -25,13 +25,24 @@ package java.lang.classfile; import java.lang.classfile.constantpool.ClassEntry; +import java.lang.constant.ClassDesc; import jdk.internal.classfile.impl.SuperclassImpl; /** - * Models the superclass of a class. Delivered as a {@link - * java.lang.classfile.ClassElement} when traversing a {@link ClassModel}. + * Models the superclass (JVMS {@jvms 4.1}) of a class. A {@code Superclass} + * appears at most once in a {@link ClassModel}: it must be absent for + * {@linkplain ClassModel#isModuleInfo() module descriptors} or the {@link + * Object} class, and must be present otherwise. A {@link ClassBuilder} sets + * the {@link Object} class as the superclass if the superclass is not supplied + * and the class to build is required to have a superclass. + *

    + * All {@linkplain ClassFile#ACC_INTERFACE interfaces} have {@link Object} as + * their superclass. * + * @see ClassModel#superclass() + * @see ClassBuilder#withSuperclass + * @jvms 4.1 The {@code ClassFile} Structure * @since 24 */ public sealed interface Superclass @@ -43,6 +54,7 @@ public sealed interface Superclass /** * {@return a {@linkplain Superclass} element} + * * @param superclassEntry the superclass */ static Superclass of(ClassEntry superclassEntry) { diff --git a/src/java.base/share/classes/java/lang/classfile/TypeKind.java b/src/java.base/share/classes/java/lang/classfile/TypeKind.java index e3fc11858f564..d912c5c37acf1 100644 --- a/src/java.base/share/classes/java/lang/classfile/TypeKind.java +++ b/src/java.base/share/classes/java/lang/classfile/TypeKind.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 @@ -108,11 +108,16 @@ public enum TypeKind { */ LONG(2, 11), /** - * The primitive type {@code float}. + * The primitive type {@code float}. All NaN values of {@code float} may or + * may not be collapsed into a single {@linkplain Float#NaN "canonical" NaN + * value} in loading and storing. */ FLOAT(1, 6), /** - * The primitive type {@code double}. It is of {@linkplain #slotSize() category} 2. + * The primitive type {@code double}. It is of {@linkplain #slotSize() + * category} 2. All NaN values of {@code double} may or may not be + * collapsed into a single {@linkplain Double#NaN "canonical" NaN value} + * in loading and storing. */ DOUBLE(2, 7), // End primitive types diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java index 4a06fe2d61019..743014704f901 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java @@ -49,6 +49,7 @@ * being built. * * @see Attributes#code() + * @see CodeModel * @jvms 4.7.3 The {@code Code} Attribute * @since 24 */ diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassInfo.java index 1394ee592edae..960d3755c4cde 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/InnerClassInfo.java @@ -68,7 +68,8 @@ public sealed interface InnerClassInfo /** * {@return a bit mask of flags denoting access permissions and properties - * of the inner class} + * of the inner class} It is in the range of unsigned short, {@code [0, + * 0xFFFF]}. * * @see Class#getModifiers() * @see AccessFlag.Location#INNER_CLASS diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/MethodParameterInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/MethodParameterInfo.java index 0366ec07e2506..636c671f25ad9 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/MethodParameterInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/MethodParameterInfo.java @@ -53,7 +53,8 @@ public sealed interface MethodParameterInfo Optional name(); /** - * {@return the access flags, as a bit mask} + * {@return the access flags, as a bit mask} It is in the range of unsigned + * short, {@code [0, 0xFFFF]}. * * @see Parameter#getModifiers() * @see AccessFlag.Location#METHOD_PARAMETER 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 231796c1b818a..ad564913d84f5 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 @@ -81,7 +81,8 @@ public sealed interface ModuleAttribute ModuleEntry moduleName(); /** - * {@return the module flags of the module, as a bit mask} + * {@return the module flags of the module, as a bit mask} It is in the + * range of unsigned short, {@code [0, 0xFFFF]}. * * @see ModuleDescriptor#modifiers() * @see AccessFlag.Location#MODULE 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 2071561a1e55e..e498b8bc036e8 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 @@ -58,6 +58,7 @@ public sealed interface ModuleExportInfo /** * {@return the flags associated with this export declaration, as a bit mask} + * It is in the range of unsigned short, {@code [0, 0xFFFF]}. * * @see ModuleDescriptor.Exports#modifiers() * @see AccessFlag.Location#MODULE_EXPORTS 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 16c0cf8a057b5..d183a0b498582 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 @@ -64,6 +64,7 @@ public sealed interface ModuleOpenInfo /** * {@return the flags associated with this open declaration, as a bit mask} + * It is in the range of unsigned short, {@code [0, 0xFFFF]}. * * @see ModuleDescriptor.Opens#modifiers() * @see AccessFlag.Location#MODULE_OPENS diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java index edffe9416295a..4954455409053 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java @@ -55,6 +55,7 @@ public sealed interface ModuleRequireInfo /** * {@return the flags associated with this require declaration, as a bit mask} + * It is in the range of unsigned short, {@code [0, 0xFFFF]}. * * @see ModuleDescriptor.Requires#modifiers() * @see AccessFlag.Location#MODULE_REQUIRES diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleResolutionAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleResolutionAttribute.java index 75e9ca485ff5b..039700f024a23 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/ModuleResolutionAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/ModuleResolutionAttribute.java @@ -78,7 +78,8 @@ public sealed interface ModuleResolutionAttribute permits BoundAttribute.BoundModuleResolutionAttribute, UnboundAttribute.UnboundModuleResolutionAttribute { /** - * {@return the module resolution flags} + * {@return the module resolution flags} It is in the range of unsigned + * short, {@code [0, 0xFFFF]}. *

    * The value of the resolution_flags item is a mask of flags used to denote * properties of module resolution. The flags are as follows: 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 260fc982dab07..b7145eeb59cb5 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 @@ -467,6 +467,9 @@ default ConstantDynamicEntry constantDynamicEntry(DynamicConstantDesc dcd) { /** * {@return a {@link FloatEntry} describing the provided value} + *

    + * All NaN values of the {@code float} may or may not be collapsed into a + * single {@linkplain Float#NaN "canonical" NaN value}. * * @param value the value * @see FloatEntry#floatValue() FloatEntry::floatValue @@ -483,6 +486,9 @@ default ConstantDynamicEntry constantDynamicEntry(DynamicConstantDesc dcd) { /** * {@return a {@link DoubleEntry} describing the provided value} + *

    + * All NaN values of the {@code double} may or may not be collapsed into a + * single {@linkplain Double#NaN "canonical" NaN value}. * * @param value the value * @see DoubleEntry#doubleValue() DoubleEntry::doubleValue diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java index 2a86c4bd09c2c..e924ca718e77d 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.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 @@ -54,6 +54,7 @@ * } * * @see CodeBuilder#exceptionCatch CodeBuilder::exceptionCatch + * @see CodeAttribute#exceptionHandlers() * @jvms 4.7.3 The {@code Code} Attribute * @since 24 */ 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 2d6a8959a2de5..b5d79ff6e7e71 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 @@ -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 @@ -25,14 +25,16 @@ /** *

    Provides classfile parsing, generation, and transformation library.

    - * The {@code java.lang.classfile} package contains classes for reading, writing, and - * modifying Java class files, as specified in Chapter {@jvms 4} of the - * Java Virtual Machine Specification. + * The {@code java.lang.classfile} package contains API models for reading, + * writing, and modifying Java class files, as specified in Chapter {@jvms 4} of + * the Java Virtual Machine Specification. This package, {@link + * java.lang.classfile.attribute}, {@link java.lang.classfile.constantpool}, + * and {@link java.lang.classfile.instruction} form the Class-File API. * *

    Reading classfiles

    - * The main class for reading classfiles is {@link java.lang.classfile.ClassModel}; we - * convert bytes into a {@link java.lang.classfile.ClassModel} with {@link - * java.lang.classfile.ClassFile#parse(byte[])}: + * The main class for reading classfiles is {@link ClassModel}; we + * convert bytes into a {@link ClassModel} with {@link + * ClassFile#parse(byte[])}: * * {@snippet lang=java : * ClassModel cm = ClassFile.of().parse(bytes); @@ -41,29 +43,32 @@ * There are several additional overloads of {@code parse} that let you specify * various processing options. *

    - * A {@link java.lang.classfile.ClassModel} is an immutable description of a class + * A {@link ClassModel} is an immutable description of a class * file. It provides accessor methods to get at class metadata (e.g., {@link - * java.lang.classfile.ClassModel#thisClass()}, {@link java.lang.classfile.ClassModel#flags()}), - * as well as subordinate classfile entities ({@link java.lang.classfile.ClassModel#fields()}, - * {@link java.lang.classfile.ClassModel#attributes()}). A {@link - * java.lang.classfile.ClassModel} is inflated lazily; most parts of the classfile are - * not parsed until they are actually needed. + * ClassModel#thisClass()}, {@link ClassModel#flags()}), + * as well as subordinate classfile entities ({@link ClassModel#fields()}, + * {@link ClassModel#attributes()}). A {@link + * ClassModel} is inflated lazily; most parts of the classfile are + * not parsed until they are actually needed. Due to the laziness, these models + * may not be thread safe. Additionally, invocations to accessor methods on + * models may lead to {@link IllegalArgumentException} due to malformed {@code + * class} file format, as parsing happens lazily. *

    * We can enumerate the names of the fields and methods in a class by: * {@snippet lang="java" class="PackageSnippets" region="enumerateFieldsMethods1"} *

    - * When we enumerate the methods, we get a {@link java.lang.classfile.MethodModel} for each method; like a + * When we enumerate the methods, we get a {@link MethodModel} for each method; like a * {@code ClassModel}, it gives us access to method metadata and * the ability to descend into subordinate entities such as the bytecodes of the * method body. In this way, a {@code ClassModel} is the root of a * tree, with children for fields, methods, and attributes, and {@code MethodModel} in * turn has its own children (attributes, {@code CodeModel}, etc.) *

    - * Methods like {@link java.lang.classfile.ClassModel#methods} allows us to traverse the class structure + * Methods like {@link ClassModel#methods} allows us to traverse the class structure * explicitly, going straight to the parts we are interested in. This is useful * for certain kinds of analysis, but if we wanted to process the whole * classfile, we may want something more organized. A {@link - * java.lang.classfile.ClassModel} also provides us with a view of the classfile as a + * ClassModel} also provides us with a view of the classfile as a * series of class elements, which may include methods, fields, attributes, * and more, and which can be distinguished with pattern matching. We could * rewrite the above example as: @@ -87,24 +92,24 @@ * models and elements. Models represent complex structures, * such as classes, methods, fields, record elements, or the code body of a * method. Models can be explored either via random-access navigation (such as - * the {@link java.lang.classfile.ClassModel#methods()} accessor) or as a linear + * the {@link ClassModel#methods()} accessor) or as a linear * sequence of elements. (Elements can in turn also be models; a {@link - * java.lang.classfile.FieldModel} is also an element of a class.) For each model type - * (e.g., {@link java.lang.classfile.MethodModel}), there is a corresponding element - * type ({@link java.lang.classfile.MethodElement}). Models and elements are immutable + * FieldModel} is also an element of a class.) For each model type + * (e.g., {@link MethodModel}), there is a corresponding element + * type ({@link MethodElement}). Models and elements are immutable * and are inflated lazily so creating a model does not necessarily require * processing its entire content. * *

    The constant pool

    * Much of the interesting content in a classfile lives in the constant - * pool. {@link java.lang.classfile.ClassModel} provides a lazily-inflated, - * read-only view of the constant pool via {@link java.lang.classfile.ClassModel#constantPool()}. + * pool. {@link ClassModel} provides a lazily-inflated, + * read-only view of the constant pool via {@link ClassModel#constantPool()}. * Descriptions of classfile content is often exposed in the form of various - * subtypes of {@link java.lang.classfile.constantpool.PoolEntry}, such as {@link - * java.lang.classfile.constantpool.ClassEntry} or {@link java.lang.classfile.constantpool.Utf8Entry}. + * subtypes of {@link PoolEntry}, such as {@link + * ClassEntry} or {@link Utf8Entry}. *

    * Constant pool entries are also exposed through models and elements; in the - * above traversal example, the {@link java.lang.classfile.instruction.InvokeInstruction} + * above traversal example, the {@link InvokeInstruction} * element exposed a method for {@code owner} that corresponds to a {@code * Constant_Class_info} entry in the constant pool. * @@ -112,9 +117,9 @@ * Much of the contents of a classfile is stored in attributes; attributes are * found on classes, methods, fields, record components, and on the {@code Code} * attribute. Most attributes are surfaced as elements; for example, {@link - * java.lang.classfile.attribute.SignatureAttribute} is a {@link - * java.lang.classfile.ClassElement}, {@link java.lang.classfile.MethodElement}, and {@link - * java.lang.classfile.FieldElement} since it can appear in all of those places, and is + * SignatureAttribute} is a {@link + * ClassElement}, {@link MethodElement}, and {@link + * FieldElement} since it can appear in all of those places, and is * included when iterating the elements of the corresponding model. *

    * Some attributes are not surfaced as elements; these are attributes that are @@ -125,77 +130,84 @@ * treated as part of the structure they are coupled to (the entries of the * {@code BootstrapMethods} attribute are treated as part of the constant pool; * line numbers and local variable metadata are modeled as elements of {@link - * java.lang.classfile.CodeModel}.) + * CodeModel}.) *

    * The {@code Code} attribute, in addition to being modeled as a {@link - * java.lang.classfile.MethodElement}, is also a model in its own right ({@link - * java.lang.classfile.CodeModel}) due to its complex structure. + * MethodElement}, is also a model in its own right ({@link + * CodeModel}) due to its complex structure. *

    * Each standard attribute has an interface (in {@code java.lang.classfile.attribute}) * which exposes the contents of the attribute and provides factories to * construct the attribute. For example, the {@code Signature} attribute is - * defined by the {@link java.lang.classfile.attribute.SignatureAttribute} class, and - * provides accessors for {@link java.lang.classfile.attribute.SignatureAttribute#signature()} - * as well as factories taking {@link java.lang.classfile.constantpool.Utf8Entry} or - * {@link java.lang.String}. + * defined by the {@link SignatureAttribute} class, and + * provides accessors for {@link SignatureAttribute#signature()} + * as well as factories taking {@link Utf8Entry} or + * {@link String}. * *

    Custom attributes

    * Attributes are converted between their classfile form and their corresponding - * object form via an {@link java.lang.classfile.AttributeMapper}. An {@code + * object form via an {@link AttributeMapper}. An {@code * AttributeMapper} provides the - * {@link java.lang.classfile.AttributeMapper#readAttribute(AttributedElement, + * {@link AttributeMapper#readAttribute(AttributedElement, * ClassReader, int)} method for mapping from the classfile format * to an attribute instance, and the - * {@link java.lang.classfile.AttributeMapper#writeAttribute(java.lang.classfile.BufWriter, - * java.lang.classfile.Attribute)} method for mapping back to the classfile format. It also + * {@link AttributeMapper#writeAttribute(BufWriter, + * Attribute)} method for mapping back to the classfile format. It also * contains metadata including the attribute name, the set of classfile entities * where the attribute is applicable, and whether multiple attributes of the * same kind are allowed on a single entity. *

    - * There are built-in attribute mappers (in {@link java.lang.classfile.Attributes}) for + * There are built-in attribute mappers (in {@link Attributes}) for * each of the attribute types defined in section {@jvms 4.7} of The Java Virtual * Machine Specification, as well as several common nonstandard attributes used by the * JDK such as {@code CharacterRangeTable}. *

    * Unrecognized attributes are delivered as elements of type {@link - * java.lang.classfile.attribute.UnknownAttribute}, which provide access only to the + * UnknownAttribute}, which provide access only to the * {@code byte[]} contents of the attribute. *

    * For nonstandard attributes, user-provided attribute mappers can be specified * through the use of the {@link - * java.lang.classfile.ClassFile.AttributeMapperOption#of(java.util.function.Function)}} + * ClassFile.AttributeMapperOption#of(Function)}} * classfile option. Implementations of custom attributes should extend {@link - * java.lang.classfile.CustomAttribute}. + * CustomAttribute}. * - *

    Options

    + *

    Options

    *

    - * {@link java.lang.classfile.ClassFile#of(java.lang.classfile.ClassFile.Option[])} - * accepts a list of options. {@link java.lang.classfile.ClassFile.Option} is a base interface + * {@link ClassFile#of(ClassFile.Option[])} + * accepts a list of options. {@link ClassFile.Option} is a base interface * for some statically enumerated options, as well as factories for more complex options, * including: *

      - *
    • {@link java.lang.classfile.ClassFile.AttributeMapperOption#of(java.util.function.Function)} + *
    • {@link ClassFile.AttributeMapperOption#of(Function)} * -- specify format of custom attributes
    • - *
    • {@link java.lang.classfile.ClassFile.AttributesProcessingOption} + *
    • {@link ClassFile.AttributesProcessingOption} * -- unrecognized or problematic original attributes (default is {@code PASS_ALL_ATTRIBUTES})
    • - *
    • {@link java.lang.classfile.ClassFile.ClassHierarchyResolverOption#of(java.lang.classfile.ClassHierarchyResolver)} + *
    • {@link ClassFile.ClassHierarchyResolverOption#of(ClassHierarchyResolver)} * -- specify a custom class hierarchy resolver used by stack map generation
    • - *
    • {@link java.lang.classfile.ClassFile.ConstantPoolSharingOption}} + *
    • {@link ClassFile.ConstantPoolSharingOption}} * -- share constant pool when transforming (default is {@code SHARED_POOL})
    • - *
    • {@link java.lang.classfile.ClassFile.DeadCodeOption}} + *
    • {@link ClassFile.DeadCodeOption}} * -- patch out unreachable code (default is {@code PATCH_DEAD_CODE})
    • - *
    • {@link java.lang.classfile.ClassFile.DeadLabelsOption}} + *
    • {@link ClassFile.DeadLabelsOption}} * -- filter unresolved labels (default is {@code FAIL_ON_DEAD_LABELS})
    • - *
    • {@link java.lang.classfile.ClassFile.DebugElementsOption} + *
    • {@link ClassFile.DebugElementsOption} * -- processing of debug information, such as local variable metadata (default is {@code PASS_DEBUG})
    • - *
    • {@link java.lang.classfile.ClassFile.LineNumbersOption} + *
    • {@link ClassFile.LineNumbersOption} * -- processing of line numbers (default is {@code PASS_LINE_NUMBERS})
    • - *
    • {@link java.lang.classfile.ClassFile.ShortJumpsOption} + *
    • {@link ClassFile.ShortJumpsOption} * -- automatically rewrite short jumps to long when necessary (default is {@code FIX_SHORT_JUMPS})
    • - *
    • {@link java.lang.classfile.ClassFile.StackMapsOption} + *
    • {@link ClassFile.StackMapsOption} * -- generate stackmaps (default is {@code STACK_MAPS_WHEN_REQUIRED})
    • *
    *

    + * {@link ClassFile.AttributeMapperOption} and {@link ClassFile.ClassHierarchyResolverOption} + * are critical to the correctness of {@code class} file parsing and generation. + * The attribute mapper is required to parse custom attributes. A correct + * resolver is required to generate {@code class} files that refer to classes + * not available to the system class loader in its bytecode, or in corner cases, + * when generation wishes to avoid loading system classes, such as in agents. + *

    * Most options allow you to request that certain parts of the classfile be * skipped during traversal, such as debug information or unrecognized * attributes. Some options allow you to suppress generation of portions of the @@ -207,8 +219,8 @@ *

    Writing classfiles

    * ClassFile generation is accomplished through builders. For each * entity type that has a model, there is also a corresponding builder type; - * classes are built through {@link java.lang.classfile.ClassBuilder}, methods through - * {@link java.lang.classfile.MethodBuilder}, etc. + * classes are built through {@link ClassBuilder}, methods through + * {@link MethodBuilder}, etc. *

    * Rather than creating builders directly, builders are provided as an argument * to a user-provided lambda. To generate the familiar "hello world" program, @@ -226,34 +238,34 @@ * Builders often support multiple ways of expressing the same entity at * different levels of abstraction. For example, the {@code invokevirtual} * instruction invoking {@code println} could have been generated with {@link - * java.lang.classfile.CodeBuilder#invokevirtual(java.lang.constant.ClassDesc, - * java.lang.String, java.lang.constant.MethodTypeDesc) CodeBuilder.invokevirtual}, {@link - * java.lang.classfile.CodeBuilder#invoke(java.lang.classfile.Opcode, - * java.lang.constant.ClassDesc, java.lang.String, java.lang.constant.MethodTypeDesc, - * boolean) CodeBuilder.invokeInstruction}, or {@link - * java.lang.classfile.CodeBuilder#with(java.lang.classfile.ClassFileElement) + * CodeBuilder#invokevirtual(ClassDesc, + * String, MethodTypeDesc) CodeBuilder.invokevirtual}, {@link + * CodeBuilder#invoke(Opcode, + * ClassDesc, String, MethodTypeDesc, + * boolean) CodeBuilder.invoke}, or {@link + * CodeBuilder#with(ClassFileElement) * CodeBuilder.with}. *

    * The convenience method {@code CodeBuilder.invokevirtual} behaves as if it calls - * the convenience method {@code CodeBuilder.invokeInstruction}, which in turn behaves + * the convenience method {@code CodeBuilder.invoke}, which in turn behaves * as if it calls method {@code CodeBuilder.with}. This composing of method calls on the * builder enables the composing of transforms (as described later). *

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

    * *

    Symbolic information

    * To describe symbolic information for classes and types, the API uses the - * nominal descriptor abstractions from {@code java.lang.constant} such as {@link - * java.lang.constant.ClassDesc} and {@link java.lang.constant.MethodTypeDesc}, + * nominal descriptor abstractions from {@link java.lang.constant} such as {@link + * ClassDesc} and {@link MethodTypeDesc}, * which is less error-prone than using raw strings. *

    * If a constant pool entry has a nominal representation then it provides a * method returning the corresponding nominal descriptor type e.g. - * method {@link java.lang.classfile.constantpool.ClassEntry#asSymbol} returns + * method {@link ClassEntry#asSymbol} returns * {@code ClassDesc}. *

    * Where appropriate builders provide two methods for building an element with @@ -266,13 +278,14 @@ * methods accepts the provided information without implicit validation. * However, fatal inconsistencies (like for example invalid code sequence or * unresolved labels) affects internal tools and may cause exceptions later in - * the classfile building process. + * the classfile building process. These fatal exceptions are thrown as + * {@link IllegalArgumentException}. *

    * Using nominal descriptors assures the right serial form is applied by the * ClassFile API library based on the actual context. Also these nominal * descriptors are validated during their construction, so it is not possible to * create them with invalid content by mistake. Following example pass class - * name to the {@link java.lang.constant.ClassDesc#of} method for validation + * name to the {@link ClassDesc#of} method for validation * and the library performs automatic conversion to the right internal form of * the class name when serialized in the constant pool as a class entry. * {@snippet lang=java : @@ -290,7 +303,7 @@ * } *

    * More complex verification of a classfile can be achieved by invocation of - * {@link java.lang.classfile.ClassFile#verify}. + * {@link ClassFile#verify}. * *

    Transforming classfiles

    * ClassFile Processing APIs are most frequently used to combine reading and @@ -301,9 +314,9 @@ * to the builder. *

    * If we wanted to strip out methods whose names starts with "debug", we could - * get an existing {@link java.lang.classfile.ClassModel}, build a new classfile that - * provides a {@link java.lang.classfile.ClassBuilder}, iterate the elements of the - * original {@link java.lang.classfile.ClassModel}, and pass through all of them to + * get an existing {@link ClassModel}, build a new classfile that + * provides a {@link ClassBuilder}, iterate the elements of the + * original {@link ClassModel}, and pass through all of them to * the builder except the methods we want to drop: * {@snippet lang="java" class="PackageSnippets" region="stripDebugMethods1"} *

    @@ -335,7 +348,7 @@ * operations can be more easily combined. Suppose we want to redirect * invocations of static methods on {@code Foo} to the corresponding method on * {@code Bar} instead. We could express this as a transformation on {@link - * java.lang.classfile.CodeElement}: + * CodeElement}: * {@snippet lang="java" class="PackageSnippets" region="fooToBarTransform"} *

    * We can then lift this transformation on code elements into a @@ -376,7 +389,7 @@ * {@snippet lang="java" class="PackageSnippets" region="instrumentCallsTransform"} *

    * Then we can compose {@code fooToBar} and {@code instrumentCalls} with {@link - * java.lang.classfile.CodeTransform#andThen(java.lang.classfile.CodeTransform)}: + * CodeTransform#andThen(CodeTransform)}: * * {@snippet lang=java : * var cc = ClassFile.of(); @@ -399,7 +412,7 @@ * attributes that are not transformed can be processed by bulk-copying their * bytes, rather than parsing them and regenerating their contents.) If * constant pool sharing is not desired it can be suppressed - * with the {@link java.lang.classfile.ClassFile.ConstantPoolSharingOption} option. + * with the {@link ClassFile.ConstantPoolSharingOption} option. * Such suppression may be beneficial when transformation removes many elements, * resulting in many unreferenced constant pool entries. * @@ -429,14 +442,14 @@ * corresponding interface to describe that element, and factory methods to * create that element. Some element kinds also have convenience methods on the * corresponding builder (e.g., {@link - * java.lang.classfile.CodeBuilder#invokevirtual(java.lang.constant.ClassDesc, - * java.lang.String, java.lang.constant.MethodTypeDesc)}). + * CodeBuilder#invokevirtual(ClassDesc, + * String, MethodTypeDesc)}). *

    * Most symbolic information in elements is represented by constant pool entries * (for example, the owner of a field is represented by a {@link - * java.lang.classfile.constantpool.ClassEntry}.) Factories and builders also - * accept nominal descriptors from {@code java.lang.constant} (e.g., {@link - * java.lang.constant.ClassDesc}.) + * ClassEntry}.) Factories and builders also + * accept nominal descriptors from {@link java.lang.constant} (e.g., {@link + * ClassDesc}.) * *

    Data model

    * We define each kind of element by its name, an optional arity indicator (zero @@ -485,7 +498,7 @@ * * Fields and methods are models with their own elements. The elements of fields * and methods are fairly simple; most of the complexity of methods lives in the - * {@link java.lang.classfile.CodeModel} (which models the {@code Code} attribute + * {@link CodeModel} (which models the {@code Code} attribute * along with the code-related attributes: stack map table, local variable table, * line number table, etc.) * @@ -502,7 +515,7 @@ * | ExceptionsAttribute?(List exceptions) * } * - * {@link java.lang.classfile.CodeModel} is unique in that its elements are ordered. + * {@link CodeModel} is unique in that its elements are ordered. * Elements of {@code Code} include ordinary bytecodes, as well as a number of pseudo-instructions * representing branch targets, line number metadata, local variable metadata, and * catch blocks. @@ -549,3 +562,13 @@ * @since 24 */ package java.lang.classfile; + +import java.lang.classfile.attribute.SignatureAttribute; +import java.lang.classfile.attribute.UnknownAttribute; +import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.PoolEntry; +import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.classfile.instruction.InvokeInstruction; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.util.function.Function; diff --git a/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java b/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java index 7a58da7f6cebc..df901b1a6ccd2 100644 --- a/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java +++ b/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.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 @@ -25,6 +25,7 @@ package java.lang.classfile.snippets; import java.lang.classfile.*; +import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.instruction.*; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; @@ -185,10 +186,14 @@ void fooToBarTransform() { // @start region="fooToBarTransform" CodeTransform fooToBar = (b, e) -> { if (e instanceof InvokeInstruction i - && i.owner().asInternalName().equals("Foo") - && i.opcode() == Opcode.INVOKESTATIC) - b.invoke(i.opcode(), CD_Bar, i.name().stringValue(), i.typeSymbol(), i.isInterface()); - else b.with(e); + && i.owner().name().equalsString("Foo") + && i.opcode() == Opcode.INVOKESTATIC) { + // remove the old element i by doing nothing to the builder + // add a new invokestatic instruction to the builder + b.invokestatic(CD_Bar, i.name().stringValue(), i.typeSymbol(), i.isInterface()); + } else { + b.with(e); // leaves the element in place + } }; // @end } @@ -324,4 +329,12 @@ void resolverExample() { ClassHierarchyResolver resolver = ClassHierarchyResolver.ofClassLoading(lookup).cached(); // @end } + + void manualReuseStackMaps(CodeBuilder cob, MethodModel method) { + // @start region="manual-reuse-stack-maps" + CodeAttribute code = method.findAttribute(Attributes.code()).orElseThrow(); + // Note that StackMapTable may be absent, representing code with no branching + code.findAttribute(Attributes.stackMapTable()).ifPresent(cob); + // @end + } }