From 990e3a700dce3441bd9506ca571c1790e57849a9 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 27 Jun 2023 05:05:17 +0000 Subject: [PATCH] 8308899: Introduce Classfile context and improve Classfile options 8306650: Improve control of stack maps generation in Classfile API 8308646: Typo in ConstantValueAttribute Reviewed-by: mcimadamore --- .../share/classes/java/lang/Module.java | 4 +- .../classfile/ClassHierarchyResolver.java | 6 +- .../jdk/internal/classfile/ClassModel.java | 19 - .../jdk/internal/classfile/ClassReader.java | 2 +- .../jdk/internal/classfile/Classfile.java | 990 ++++++++++-------- .../classfile/ClassfileTransform.java | 2 +- .../internal/classfile/PseudoInstruction.java | 2 +- .../attribute/ConstantValueAttribute.java | 2 +- .../attribute/LineNumberTableAttribute.java | 2 +- .../LocalVariableTableAttribute.java | 2 +- .../LocalVariableTypeTableAttribute.java | 2 +- .../classfile/components/ClassRemapper.java | 6 +- .../snippet-files/PackageSnippets.java | 32 +- .../constantpool/ConstantPoolBuilder.java | 23 +- .../classfile/impl/AbstractDirectBuilder.java | 4 +- .../classfile/impl/AnnotationImpl.java | 28 +- .../classfile/impl/AnnotationReader.java | 26 +- .../classfile/impl/BoundAttribute.java | 2 +- .../classfile/impl/BufWriterImpl.java | 16 +- .../classfile/impl/BufferedCodeBuilder.java | 5 +- .../classfile/impl/BufferedFieldBuilder.java | 5 +- .../classfile/impl/BufferedMethodBuilder.java | 11 +- .../classfile/impl/ChainedClassBuilder.java | 8 +- .../classfile/impl/ClassHierarchyImpl.java | 19 +- .../internal/classfile/impl/ClassImpl.java | 23 +- .../classfile/impl/ClassReaderImpl.java | 12 +- .../classfile/impl/ClassfileImpl.java | 138 +++ .../jdk/internal/classfile/impl/CodeImpl.java | 5 +- .../classfile/impl/DirectClassBuilder.java | 21 +- .../classfile/impl/DirectCodeBuilder.java | 132 ++- .../classfile/impl/DirectFieldBuilder.java | 3 +- .../classfile/impl/DirectMethodBuilder.java | 7 +- .../jdk/internal/classfile/impl/Options.java | 82 -- .../classfile/impl/SplitConstantPool.java | 24 - .../classfile/impl/StackMapGenerator.java | 41 +- .../classfile/instruction/CharacterRange.java | 2 +- .../instruction/ConstantInstruction.java | 4 +- .../classfile/instruction/LineNumber.java | 2 +- .../classfile/instruction/LocalVariable.java | 2 +- .../instruction/LocalVariableType.java | 2 +- .../jdk/internal/classfile/package-info.java | 53 +- .../snippet-files/PackageSnippets.java | 32 +- .../foreign/abi/BindingSpecializer.java | 4 +- .../internal/module/ModuleInfoExtender.java | 5 +- .../classes/sun/tools/jar/FingerPrint.java | 2 +- .../classes/jdk/tools/jimage/JImageTask.java | 2 +- .../internal/plugins/AbstractPlugin.java | 4 +- .../StripJavaDebugAttributesPlugin.java | 17 +- .../internal/plugins/SystemModulesPlugin.java | 6 +- .../internal/plugins/VersionPropsPlugin.java | 4 +- .../execution/LocalExecutionControl.java | 7 +- .../lang/module/ModuleDescriptorTest.java | 2 +- test/jdk/jdk/classfile/AdaptCodeTest.java | 20 +- .../AdvancedTransformationsTest.java | 35 +- .../jdk/classfile/AnnotationModelTest.java | 2 +- test/jdk/jdk/classfile/AnnotationTest.java | 10 +- test/jdk/jdk/classfile/ArrayTest.java | 2 +- test/jdk/jdk/classfile/BSMTest.java | 5 +- test/jdk/jdk/classfile/BasicBlockTest.java | 5 +- .../jdk/jdk/classfile/BoundAttributeTest.java | 5 +- test/jdk/jdk/classfile/BuilderBlockTest.java | 24 +- test/jdk/jdk/classfile/BuilderParamTest.java | 7 +- .../jdk/classfile/BuilderTryCatchTest.java | 8 +- test/jdk/jdk/classfile/ClassBuildingTest.java | 5 +- .../jdk/classfile/ClassHierarchyInfoTest.java | 6 +- test/jdk/jdk/classfile/ClassPrinterTest.java | 3 +- .../jdk/classfile/ConstantPoolCopyTest.java | 3 +- test/jdk/jdk/classfile/CorpusTest.java | 28 +- .../DiscontinuedInstructionsTest.java | 24 +- .../jdk/classfile/FilterDeadLabelsTest.java | 5 +- test/jdk/jdk/classfile/LDCTest.java | 5 +- test/jdk/jdk/classfile/LimitsTest.java | 8 +- test/jdk/jdk/classfile/LowAdaptTest.java | 5 +- .../jdk/classfile/LowJCovAttributeTest.java | 2 +- test/jdk/jdk/classfile/LowModuleTest.java | 2 +- test/jdk/jdk/classfile/LvtTest.java | 25 +- .../jdk/classfile/MassAdaptCopyCodeTest.java | 4 +- .../MassAdaptCopyPrimitiveMatchCodeTest.java | 7 +- test/jdk/jdk/classfile/ModuleBuilderTest.java | 12 +- test/jdk/jdk/classfile/OneToOneTest.java | 6 +- .../jdk/classfile/OpcodesValidationTest.java | 4 +- .../classfile/PrimitiveClassConstantTest.java | 2 +- test/jdk/jdk/classfile/ShortJumpsFixTest.java | 96 +- test/jdk/jdk/classfile/SignaturesTest.java | 2 +- test/jdk/jdk/classfile/SnippetsTest.java | 69 ++ test/jdk/jdk/classfile/StackMapsTest.java | 41 +- test/jdk/jdk/classfile/StackTrackerTest.java | 4 +- .../jdk/jdk/classfile/StreamedVsListTest.java | 2 +- test/jdk/jdk/classfile/SwapTest.java | 2 +- .../TempConstantPoolBuilderTest.java | 5 +- .../jdk/classfile/TestRecordComponent.java | 17 +- test/jdk/jdk/classfile/TransformTests.java | 21 +- test/jdk/jdk/classfile/Utf8EntryTest.java | 6 +- test/jdk/jdk/classfile/VerifierSelfTest.java | 9 +- test/jdk/jdk/classfile/WriteTest.java | 4 +- .../examples/AnnotationsExamples.java | 12 +- .../classfile/examples/ExampleGallery.java | 36 +- .../ExperimentalTransformExamples.java | 2 +- .../classfile/examples/ModuleExamples.java | 7 +- .../classfile/examples/TransformExamples.java | 11 +- .../helpers/RebuildingTransformation.java | 2 +- .../jdk/jdk/classfile/helpers/Transforms.java | 50 +- .../jdk/test/lib/util/ModuleInfoWriter.java | 2 +- .../bench/jdk/classfile/AdHocAdapt.java | 3 +- .../jdk/classfile/ClassfileBenchmark.java | 118 +++ .../jdk/classfile/GenerateStackMaps.java | 6 +- .../bench/jdk/classfile/ParseOptions.java | 15 +- .../openjdk/bench/jdk/classfile/ReadDeep.java | 6 +- .../bench/jdk/classfile/ReadMetadata.java | 9 +- .../jdk/classfile/RebuildMethodBodies.java | 49 +- .../jdk/classfile/RepeatedModelTraversal.java | 3 +- .../bench/jdk/classfile/Transforms.java | 44 +- .../openjdk/bench/jdk/classfile/Write.java | 4 +- 113 files changed, 1602 insertions(+), 1190 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/classfile/impl/ClassfileImpl.java delete mode 100644 src/java.base/share/classes/jdk/internal/classfile/impl/Options.java create mode 100644 test/jdk/jdk/classfile/SnippetsTest.java create mode 100644 test/micro/org/openjdk/bench/jdk/classfile/ClassfileBenchmark.java diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 84da07638c7..33e92527488 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1590,8 +1590,8 @@ public final class Module implements AnnotatedElement { */ private Class loadModuleInfoClass(InputStream in) throws IOException { final String MODULE_INFO = "module-info"; - byte[] bytes = Classfile.parse(in.readAllBytes(), - Classfile.Option.constantPoolSharing(false)).transform((clb, cle) -> { + var cc = Classfile.of(Classfile.ConstantPoolSharingOption.NEW_POOL); + byte[] bytes = cc.transform(cc.parse(in.readAllBytes()), (clb, cle) -> { switch (cle) { case AccessFlags af -> clb.withFlags(AccessFlag.INTERFACE, AccessFlag.ABSTRACT, AccessFlag.SYNTHETIC); diff --git a/src/java.base/share/classes/jdk/internal/classfile/ClassHierarchyResolver.java b/src/java.base/share/classes/jdk/internal/classfile/ClassHierarchyResolver.java index 45c9058e72d..bc0a6423971 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/ClassHierarchyResolver.java +++ b/src/java.base/share/classes/jdk/internal/classfile/ClassHierarchyResolver.java @@ -48,10 +48,8 @@ import static java.lang.constant.ConstantDescs.CD_Object; public interface ClassHierarchyResolver { /** - * Returns a default instance of {@linkplain ClassHierarchyResolver} - * that reads from system class loader with - * {@link ClassLoader#getSystemResourceAsStream(String)} and falls - * back to reflection if a class is not found. + * Returns a default instance of {@linkplain ClassHierarchyResolver} that + * gets {@link ClassHierarchyInfo} from system class loader with reflection. */ static ClassHierarchyResolver defaultResolver() { return ClassHierarchyImpl.DEFAULT_RESOLVER; diff --git a/src/java.base/share/classes/jdk/internal/classfile/ClassModel.java b/src/java.base/share/classes/jdk/internal/classfile/ClassModel.java index b2371cca7a6..e76727f96a5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/ClassModel.java +++ b/src/java.base/share/classes/jdk/internal/classfile/ClassModel.java @@ -72,25 +72,6 @@ public sealed interface ClassModel /** {@return the interfaces implemented by this class} */ List interfaces(); - /** - * Transform this classfile into a new classfile with the aid of 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 : - * Classfile.build(thisClass(), ConstantPoolBuilder.of(this), - * b -> b.transform(this, transform)); - * } - * - * @param transform the transform - * @return the bytes of the new class - */ - byte[] transform(ClassTransform transform); - /** {@return whether this class is a module descriptor} */ boolean isModuleInfo(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/ClassReader.java b/src/java.base/share/classes/jdk/internal/classfile/ClassReader.java index ad8e4b57771..7904038e41d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/ClassReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/ClassReader.java @@ -52,7 +52,7 @@ public sealed interface ClassReader extends ConstantPool /** * {@return the table of custom attribute mappers} This is derived from - * the processing option {@link Classfile.Option#attributeMapper(Function)}. + * the processing option {@link Classfile.AttributeMapperOption}. */ Function> customAttributes(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/Classfile.java b/src/java.base/share/classes/jdk/internal/classfile/Classfile.java index 5338533c37e..7b2910e1059 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/Classfile.java +++ b/src/java.base/share/classes/jdk/internal/classfile/Classfile.java @@ -28,10 +28,6 @@ import java.io.IOException; import java.lang.constant.ClassDesc; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; import java.util.function.Consumer; import java.util.function.Function; @@ -39,140 +35,199 @@ import jdk.internal.classfile.attribute.ModuleAttribute; import jdk.internal.classfile.attribute.UnknownAttribute; import jdk.internal.classfile.constantpool.ClassEntry; import jdk.internal.classfile.constantpool.ConstantPoolBuilder; -import jdk.internal.classfile.constantpool.PackageEntry; import jdk.internal.classfile.constantpool.Utf8Entry; -import jdk.internal.classfile.impl.ClassImpl; -import jdk.internal.classfile.impl.AbstractPoolEntry; -import jdk.internal.classfile.impl.DirectClassBuilder; -import jdk.internal.classfile.impl.Options; -import jdk.internal.classfile.impl.SplitConstantPool; -import jdk.internal.classfile.impl.UnboundAttribute; +import jdk.internal.classfile.impl.ClassfileImpl; +import jdk.internal.classfile.impl.TemporaryConstantPool; import java.lang.reflect.AccessFlag; import jdk.internal.classfile.attribute.CharacterRangeInfo; import jdk.internal.classfile.attribute.LocalVariableInfo; import jdk.internal.classfile.attribute.LocalVariableTypeInfo; import jdk.internal.classfile.instruction.ExceptionCatch; -import java.lang.constant.PackageDesc; +import static java.util.Objects.requireNonNull; /** - * Main entry points for parsing, transforming, and generating classfiles. + * 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. */ -public class Classfile { - private Classfile() { +public sealed interface Classfile + permits ClassfileImpl { + + /** + * {@return a context with default options} + */ + static Classfile of() { + return ClassfileImpl.DEFAULT_CONTEXT; } /** - * An option that affects the writing of classfiles. + * {@return a new context with options altered from the default} + * @param options the desired processing options */ - public sealed interface Option permits Options.OptionValue { + static Classfile of(Option... options) { + return of().withOptions(options); + } + + /** + * {@return a copy of the context with altered options} + * @param options the desired processing options + */ + Classfile withOptions(Option... options); + + /** + * An option that affects the parsing and writing of classfiles. + */ + sealed interface Option { + } + + /** + * Option describing attribute mappers for custom attributes. + * Default is only to process standard attributes. + */ + sealed interface AttributeMapperOption extends Option + permits ClassfileImpl.AttributeMapperOptionImpl { /** - * {@return an option describing whether or not to generate stackmaps} - * Default is to generate stack maps. - * @param b whether to generate stack maps + * {@return an option describing attribute mappers for custom attributes} + * @param attributeMapper a function mapping attribute names to attribute mappers */ - static Option generateStackmap(boolean b) { return new Options.OptionValue(Options.Key.GENERATE_STACK_MAPS, b); } + static AttributeMapperOption of(Function> attributeMapper) { + requireNonNull(attributeMapper); + return new ClassfileImpl.AttributeMapperOptionImpl(attributeMapper); + } - /** - * {@return an 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 to process debug elements. - * @param b whether or not to process debug elements - */ - static Option processDebug(boolean b) { return new Options.OptionValue(Options.Key.PROCESS_DEBUG, b); } + Function> attributeMapper(); + } - /** - * {@return an option describing whether to process or discard line numbers} - * Discarding line numbers may reduce the overhead of parsing or transforming - * classfiles. - * Default is to process line numbers. - * @param b whether or not to process line numbers - */ - static Option processLineNumbers(boolean b) { return new Options.OptionValue(Options.Key.PROCESS_LINE_NUMBERS, b); } - - /** - * {@return an option describing whether to process or discard unrecognized - * attributes} - * Default is to process unrecognized attributes, and deliver as instances - * of {@link UnknownAttribute}. - * @param b whether or not to process unrecognized attributes - */ - static Option processUnknownAttributes(boolean b) { return new Options.OptionValue(Options.Key.PROCESS_UNKNOWN_ATTRIBUTES, b); } - - /** - * {@return an option describing whether to preserve the original constant - * pool when transforming a classfile} Reusing the constant pool enables significant - * optimizations in processing time and minimizes differences between the - * original and transformed classfile, but may result in a bigger classfile - * when a classfile is significantly transformed. - * Default is to preserve the original constant pool. - * @param b whether or not to preserve the original constant pool - */ - static Option constantPoolSharing(boolean b) { return new Options.OptionValue(Options.Key.CP_SHARING, b); } - - /** - * {@return an option describing whether or not to automatically rewrite - * short jumps to long when necessary} - * Default is to automatically rewrite jump instructions. - * @param b whether or not to automatically rewrite short jumps to long when necessary - */ - static Option fixShortJumps(boolean b) { return new Options.OptionValue(Options.Key.FIX_SHORT_JUMPS, b); } - - /** - * {@return an option describing whether or not to patch out unreachable code} - * Default is to automatically patch out unreachable code with NOPs. - * @param b whether or not to automatically patch out unreachable code - */ - static Option patchDeadCode(boolean b) { return new Options.OptionValue(Options.Key.PATCH_DEAD_CODE, b); } + /** + * Option describing the class hierarchy resolver to use when generating + * stack maps. + */ + sealed interface ClassHierarchyResolverOption extends Option + permits ClassfileImpl.ClassHierarchyResolverOptionImpl { /** * {@return an option describing the class hierarchy resolver to use when * generating stack maps} - * @param r the resolver + * @param classHierarchyResolver the resolver */ - static Option classHierarchyResolver(ClassHierarchyResolver r) { return new Options.OptionValue(Options.Key.HIERARCHY_RESOLVER, r); } + static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyResolver) { + requireNonNull(classHierarchyResolver); + return new ClassfileImpl.ClassHierarchyResolverOptionImpl(classHierarchyResolver); + } - /** - * {@return an option describing attribute mappers for custom attributes} - * Default is only to process standard attributes. - * @param r a function mapping attribute names to attribute mappers - */ - static Option attributeMapper(Function> r) { return new Options.OptionValue(Options.Key.ATTRIBUTE_MAPPER, r); } + ClassHierarchyResolver classHierarchyResolver(); + } - /** - * {@return an option describing whether or not to filter unresolved labels} - * Default is 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 true filters the above elements instead. - * @param b whether or not to automatically patch out unreachable code - */ - static Option filterDeadLabels(boolean b) { return new Options.OptionValue(Options.Key.FILTER_DEAD_LABELS, b); } + /** + * Option describing whether to preserve the original constant pool when + * transforming a classfile. Reusing the constant pool enables significant + * optimizations in processing time and minimizes differences between the + * original and transformed classfile, but may result in a bigger classfile + * when a classfile is significantly transformed. + * Default is {@code SHARED_POOL} to preserve the original constant + * pool. + */ + enum ConstantPoolSharingOption implements Option { + SHARED_POOL, + NEW_POOL + } + + /** + * Option describing whether or not to patch out unreachable code. + * Default is {@code PATCH_DEAD_CODE} to automatically patch out unreachable + * code with NOPs. + */ + enum DeadCodeOption implements Option { + PATCH_DEAD_CODE, + KEEP_DEAD_CODE + } + + /** + * Option describing whether or not to filter unresolved labels. + * Default is {@code FAIL_ON_DEAD_LABELS} to throw IllegalStateException + * 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. + */ + enum DeadLabelsOption implements Option { + FAIL_ON_DEAD_LABELS, + 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. + */ + enum DebugElementsOption implements Option { + PASS_DEBUG, + DROP_DEBUG + } + + /** + * Option describing whether to process or discard line numbers. + * Discarding line numbers may reduce the overhead of parsing or transforming + * classfiles. + * Default is {@code PASS_LINE_NUMBERS} to process line numbers. + */ + enum LineNumbersOption implements Option { + PASS_LINE_NUMBERS, + DROP_LINE_NUMBERS; + } + + /** + * Option describing whether or not to automatically rewrite short jumps to + * long when necessary. + * Default is {@code FIX_SHORT_JUMPS} to automatically rewrite jump + * instructions. + */ + enum ShortJumpsOption implements Option { + FIX_SHORT_JUMPS, + FAIL_ON_SHORT_JUMPS + } + + /** + * Option describing whether or not 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 + */ + enum StackMapsOption implements Option { + STACK_MAPS_WHEN_REQUIRED, + GENERATE_STACK_MAPS, + DROP_STACK_MAPS + } + + /** + * Option describing whether to process or discard unrecognized attributes. + * Default is {@code PASS_UNKNOWN_ATTRIBUTES} to process unrecognized + * attributes, and deliver as instances of {@link UnknownAttribute}. + */ + enum UnknownAttributesOption implements Option { + PASS_UNKNOWN_ATTRIBUTES, + DROP_UNKNOWN_ATTRIBUTES } /** * Parse a classfile into a {@link ClassModel}. * @param bytes the bytes of the classfile - * @param options the desired processing options * @return the class model */ - public static ClassModel parse(byte[] bytes, Option... options) { - Collection

The subtypes of {@linkplain diff --git a/src/java.base/share/classes/jdk/internal/classfile/PseudoInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/PseudoInstruction.java index 8711e80747b..d419e2314e9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/PseudoInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/PseudoInstruction.java @@ -39,7 +39,7 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction; * 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.Option#processDebug(boolean)}). + * options (e.g., {@link Classfile.DebugElementsOption}). */ public sealed interface PseudoInstruction extends CodeElement diff --git a/src/java.base/share/classes/jdk/internal/classfile/attribute/ConstantValueAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/attribute/ConstantValueAttribute.java index 3fcdb624496..a0fecb03dba 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/attribute/ConstantValueAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/attribute/ConstantValueAttribute.java @@ -67,7 +67,7 @@ public sealed interface ConstantValueAttribute case Long l -> TemporaryConstantPool.INSTANCE.longEntry(l); case Double d -> TemporaryConstantPool.INSTANCE.doubleEntry(d); case String s -> TemporaryConstantPool.INSTANCE.stringEntry(s); - default -> throw new IllegalArgumentException("Invalid ConstantValueAtrtibute value: " + value); + default -> throw new IllegalArgumentException("Invalid ConstantValueAttribute value: " + value); }); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/attribute/LineNumberTableAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/attribute/LineNumberTableAttribute.java index d66be5a6154..9314dedc6c8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/attribute/LineNumberTableAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/attribute/LineNumberTableAttribute.java @@ -36,7 +36,7 @@ import jdk.internal.classfile.impl.UnboundAttribute; * the code table and line numbers in the source file. * Delivered as a {@link jdk.internal.classfile.instruction.LineNumber} when traversing the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the - * {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option. + * {@link jdk.internal.classfile.Classfile.LineNumbersOption} option. */ public sealed interface LineNumberTableAttribute extends Attribute diff --git a/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTableAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTableAttribute.java index 76fae7e8476..e2cd49e8e59 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTableAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTableAttribute.java @@ -36,7 +36,7 @@ import java.util.List; * variables. * Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the - * {@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)} option. + * {@link jdk.internal.classfile.Classfile.DebugElementsOption} option. */ public sealed interface LocalVariableTableAttribute extends Attribute diff --git a/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTypeTableAttribute.java index 481d02829e5..cbe1257ad54 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTypeTableAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -37,7 +37,7 @@ import java.util.List; * variables. * Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the - * {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option. + * {@link jdk.internal.classfile.Classfile.LineNumbersOption} option. */ public sealed interface LocalVariableTypeTableAttribute extends Attribute diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java index ff65d9aedcd..950f0aab650 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java @@ -96,11 +96,11 @@ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapp /** * Remaps the whole ClassModel into a new class file, including the class name. + * @param context Classfile context * @param clm class model to re-map * @return re-mapped class file bytes */ - default byte[] remapClass(ClassModel clm) { - return Classfile.build(map(clm.thisClass().asSymbol()), - clb -> clm.forEachElement(resolve(clb).consumer())); + default byte[] remapClass(Classfile context, ClassModel clm) { + return context.transform(clm, map(clm.thisClass().asSymbol()), this); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java b/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java index 7ea16a3d9fd..2291e855474 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java @@ -30,6 +30,7 @@ import java.util.ArrayDeque; import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; +import jdk.internal.classfile.Classfile; import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.CodeModel; @@ -101,9 +102,9 @@ class PackageSnippets { // @start region="singleClassRemap" var classRemapper = ClassRemapper.of( Map.of(ClassDesc.of("Foo"), ClassDesc.of("Bar"))); - + var cc = Classfile.of(); for (var classModel : allMyClasses) { - byte[] newBytes = classRemapper.remapClass(classModel); + byte[] newBytes = classRemapper.remapClass(cc, classModel); } // @end @@ -113,9 +114,9 @@ class PackageSnippets { // @start region="allPackageRemap" var classRemapper = ClassRemapper.of(cd -> ClassDesc.ofDescriptor(cd.descriptorString().replace("Lcom/oldpackage/", "Lcom/newpackage/"))); - + var cc = Classfile.of(); for (var classModel : allMyClasses) { - byte[] newBytes = classRemapper.remapClass(classModel); + byte[] newBytes = classRemapper.remapClass(cc, classModel); } // @end @@ -123,20 +124,23 @@ class PackageSnippets { void codeLocalsShifting(ClassModel classModel) { // @start region="codeLocalsShifting" - byte[] newBytes = classModel.transform((classBuilder, classElement) -> { - if (classElement instanceof MethodModel method) - classBuilder.transformMethod(method, - MethodTransform.transformingCode( - CodeLocalsShifter.of(method.flags(), method.methodTypeSymbol()))); - else - classBuilder.accept(classElement); - }); + byte[] newBytes = Classfile.of().transform( + classModel, + (classBuilder, classElement) -> { + if (classElement instanceof MethodModel method) + classBuilder.transformMethod(method, + MethodTransform.transformingCode( + CodeLocalsShifter.of(method.flags(), method.methodTypeSymbol()))); + else + classBuilder.accept(classElement); + }); // @end } void codeRelabeling(ClassModel classModel) { // @start region="codeRelabeling" - byte[] newBytes = classModel.transform( + byte[] newBytes = Classfile.of().transform( + classModel, ClassTransform.transformingMethodBodies( CodeTransform.ofStateful(CodeRelabeler::of))); // @end @@ -150,7 +154,7 @@ class PackageSnippets { var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); - return target.transform( + return Classfile.of().transform(target, ClassTransform.transformingMethods( instrumentedMethodsFilter, (mb, me) -> { diff --git a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java index a757624834c..15b40637041 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java @@ -39,7 +39,7 @@ import jdk.internal.classfile.ClassBuilder; import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.Classfile; import jdk.internal.classfile.impl.ClassReaderImpl; -import jdk.internal.classfile.impl.Options; +import jdk.internal.classfile.impl.ClassfileImpl; import java.lang.constant.ModuleDesc; import java.lang.constant.PackageDesc; import jdk.internal.classfile.WritableElement; @@ -63,29 +63,22 @@ public sealed interface ConstantPoolBuilder permits SplitConstantPool, TemporaryConstantPool { /** - * {@return a new constant pool builder} The new constant pool builder - * will inherit the classfile processing options of the specified class. - * If the processing options include {@link Classfile.Option#constantPoolSharing(boolean)}, - * (the default) the new constant pool builder will be also be pre-populated with the - * contents of the constant pool associated with the class reader. + * {@return a new constant pool builder} The new constant pool builder will + * be pre-populated with the contents of the constant pool associated with + * the class reader. * * @param classModel the class to copy from */ static ConstantPoolBuilder of(ClassModel classModel) { - ClassReaderImpl reader = (ClassReaderImpl) classModel.constantPool(); - return reader.options().cpSharing - ? new SplitConstantPool(reader) - : new SplitConstantPool(reader.options()); + return new SplitConstantPool((ClassReaderImpl) classModel.constantPool()); } /** * {@return a new constant pool builder} The new constant pool builder - * will be empty and have the specified classfile processing options. - * - * @param options the processing options + * will be empty. */ - static ConstantPoolBuilder of(Collection options) { - return new SplitConstantPool(new Options(options)); + static ConstantPoolBuilder of() { + return new SplitConstantPool(); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java index 6a167dd091a..ed9268f5372 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java @@ -30,11 +30,13 @@ import jdk.internal.classfile.Attribute; public class AbstractDirectBuilder { protected final SplitConstantPool constantPool; + protected final ClassfileImpl context; protected final AttributeHolder attributes = new AttributeHolder(); protected M original; - public AbstractDirectBuilder(SplitConstantPool constantPool) { + public AbstractDirectBuilder(SplitConstantPool constantPool, ClassfileImpl context) { this.constantPool = constantPool; + this.context = context; } public SplitConstantPool constantPool() { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java index 8e508669541..b0d83786314 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java @@ -30,6 +30,8 @@ import jdk.internal.classfile.constantpool.*; import java.lang.constant.ConstantDesc; import java.util.List; +import static jdk.internal.classfile.Classfile.*; + public final class AnnotationImpl implements Annotation { private final Utf8Entry className; private final List elements; @@ -113,7 +115,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 's'; + return AEV_STRING; } @Override @@ -127,7 +129,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'D'; + return AEV_DOUBLE; } @Override @@ -141,7 +143,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'F'; + return AEV_FLOAT; } @Override @@ -155,7 +157,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'J'; + return AEV_LONG; } @Override @@ -169,7 +171,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'I'; + return AEV_INT; } @Override @@ -183,7 +185,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'S'; + return AEV_SHORT; } @Override @@ -197,7 +199,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'C'; + return AEV_CHAR; } @Override @@ -211,7 +213,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'B'; + return AEV_BYTE; } @Override @@ -225,7 +227,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return 'Z'; + return AEV_BOOLEAN; } @Override @@ -243,7 +245,7 @@ public final class AnnotationImpl implements Annotation { @Override public char tag() { - return '['; + return AEV_ARRAY; } @Override @@ -258,7 +260,7 @@ public final class AnnotationImpl implements Annotation { implements AnnotationValue.OfEnum { @Override public char tag() { - return 'e'; + return AEV_ENUM; } @Override @@ -274,7 +276,7 @@ public final class AnnotationImpl implements Annotation { implements AnnotationValue.OfAnnotation { @Override public char tag() { - return '@'; + return AEV_ANNOTATION; } @Override @@ -289,7 +291,7 @@ public final class AnnotationImpl implements Annotation { implements AnnotationValue.OfClass { @Override public char tag() { - return 'c'; + return AEV_CLASS; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index c11f07e6024..ca4e0b1228b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -58,19 +58,19 @@ class AnnotationReader { char tag = (char) classReader.readU1(p); ++p; return switch (tag) { - case 'B' -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p)); - case 'C' -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p)); - case 'D' -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p)); - case 'F' -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p)); - case 'I' -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p)); - case 'J' -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p)); - case 'S' -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p)); - case 'Z' -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p)); - case 's' -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p)); - case 'e' -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2)); - case 'c' -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p)); - case '@' -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p)); - case '[' -> { + case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p)); + case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p)); + case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p)); + case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p)); + case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p)); + case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p)); + case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p)); + case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p)); + case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p)); + case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2)); + case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p)); + case AEV_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p)); + case AEV_ARRAY -> { int numValues = classReader.readU2(p); p += 2; var values = new Object[numValues]; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java index 4c69f17e418..9b8c579e9e2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java @@ -144,7 +144,7 @@ public abstract sealed class BoundAttribute> } if (mapper != null) { filled[i] = mapper.readAttribute(enclosing, reader, p); - } else if (((ClassReaderImpl)reader).options().processUnknownAttributes) { + } else if (((ClassReaderImpl)reader).context().unknownAttributesOption() == Classfile.UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES) { AttributeMapper fakeMapper = new AttributeMapper<>() { @Override public String name() { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 8873d965402..02ae93d4e74 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -39,22 +39,24 @@ import jdk.internal.classfile.constantpool.PoolEntry; public final class BufWriterImpl implements BufWriter { private final ConstantPoolBuilder constantPool; + private final ClassfileImpl context; private LabelContext labelContext; private final ClassEntry thisClass; private final int majorVersion; byte[] elems; int offset = 0; - public BufWriterImpl(ConstantPoolBuilder constantPool) { - this(constantPool, 64, null, 0); + public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context) { + this(constantPool, context, 64, null, 0); } - public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize) { - this(constantPool, initialSize, null, 0); + public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize) { + this(constantPool, context, initialSize, null, 0); } - public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize, ClassEntry thisClass, int majorVersion) { + public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) { this.constantPool = constantPool; + this.context = context; elems = new byte[initialSize]; this.thisClass = thisClass; this.majorVersion = majorVersion; @@ -85,6 +87,10 @@ public final class BufWriterImpl implements BufWriter { return majorVersion; } + public ClassfileImpl context() { + return context; + } + @Override public void writeU1(int x) { writeIntBytes(1, x); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index 77c5b6ac57a..cfd54d9e412 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -45,6 +45,7 @@ import java.util.function.Consumer; public final class BufferedCodeBuilder implements TerminalCodeBuilder, LabelContext { private final SplitConstantPool constantPool; + private final ClassfileImpl context; private final List elements = new ArrayList<>(); private final LabelImpl startLabel, endLabel; private final CodeModel original; @@ -54,8 +55,10 @@ public final class BufferedCodeBuilder public BufferedCodeBuilder(MethodInfo methodInfo, SplitConstantPool constantPool, + ClassfileImpl context, CodeModel original) { this.constantPool = constantPool; + this.context = context; this.startLabel = new LabelImpl(this, -1); this.endLabel = new LabelImpl(this, -1); this.original = original; @@ -204,7 +207,7 @@ public final class BufferedCodeBuilder } public void writeTo(BufWriter buf) { - DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, null).writeTo(buf); + DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, context, null).writeTo(buf); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java index b2792f2627f..e6d63bc803c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java @@ -36,6 +36,7 @@ import jdk.internal.classfile.constantpool.Utf8Entry; public final class BufferedFieldBuilder implements TerminalFieldBuilder { private final SplitConstantPool constantPool; + private final ClassfileImpl context; private final Utf8Entry name; private final Utf8Entry desc; private final List elements = new ArrayList<>(); @@ -43,10 +44,12 @@ public final class BufferedFieldBuilder private final FieldModel original; public BufferedFieldBuilder(SplitConstantPool constantPool, + ClassfileImpl context, Utf8Entry name, Utf8Entry type, FieldModel original) { this.constantPool = constantPool; + this.context = context; this.name = name; this.desc = type; this.flags = AccessFlags.ofField(); @@ -119,7 +122,7 @@ public final class BufferedFieldBuilder @Override public void writeTo(BufWriter buf) { - DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, name, desc, null); + DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, context, name, desc, null); elements.forEach(fb); fb.writeTo(buf); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index de6042ef096..40edf35640a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -47,6 +47,7 @@ public final class BufferedMethodBuilder implements TerminalMethodBuilder, MethodInfo { private final List elements; private final SplitConstantPool constantPool; + private final ClassfileImpl context; private final Utf8Entry name; private final Utf8Entry desc; private AccessFlags flags; @@ -55,11 +56,13 @@ public final class BufferedMethodBuilder MethodTypeDesc mDesc; public BufferedMethodBuilder(SplitConstantPool constantPool, + ClassfileImpl context, Utf8Entry nameInfo, Utf8Entry typeInfo, MethodModel original) { this.elements = new ArrayList<>(); this.constantPool = constantPool; + this.context = context; this.name = nameInfo; this.desc = typeInfo; this.flags = AccessFlags.ofMethod(); @@ -119,21 +122,21 @@ public final class BufferedMethodBuilder @Override public MethodBuilder withCode(Consumer handler) { - return with(new BufferedCodeBuilder(this, constantPool, null) + return with(new BufferedCodeBuilder(this, constantPool, context, null) .run(handler) .toModel()); } @Override public MethodBuilder transformCode(CodeModel code, CodeTransform transform) { - BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, code); + BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, context, code); builder.transform(code, transform); return with(builder.toModel()); } @Override public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) { - return new BufferedCodeBuilder(this, constantPool, original); + return new BufferedCodeBuilder(this, constantPool, context, original); } public BufferedMethodBuilder run(Consumer handler) { @@ -204,7 +207,7 @@ public final class BufferedMethodBuilder @Override public void writeTo(BufWriter buf) { - DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, name, desc, methodFlags(), null); + DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, context, name, desc, methodFlags(), null); elements.forEach(mb); mb.writeTo(buf); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index 6c76dbcbcff..7c612158b77 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -60,7 +60,7 @@ public final class ChainedClassBuilder @Override public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer handler) { - return downstream.with(new BufferedFieldBuilder(terminal.constantPool, + return downstream.with(new BufferedFieldBuilder(terminal.constantPool, terminal.context, name, descriptor, null) .run(handler) .toModel()); @@ -68,7 +68,7 @@ public final class ChainedClassBuilder @Override public ClassBuilder transformField(FieldModel field, FieldTransform transform) { - BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool, + BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool, terminal.context, field.fieldName(), field.fieldType(), field); builder.transform(field, transform); @@ -78,7 +78,7 @@ public final class ChainedClassBuilder @Override public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags, Consumer handler) { - return downstream.with(new BufferedMethodBuilder(terminal.constantPool, + return downstream.with(new BufferedMethodBuilder(terminal.constantPool, terminal.context, name, descriptor, null) .run(handler) .toModel()); @@ -86,7 +86,7 @@ public final class ChainedClassBuilder @Override public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { - BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, + BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, terminal.context, method.methodName(), method.methodType(), method); builder.transform(method, transform); return downstream.with(builder.toModel()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index 9959f65cdee..04a252710ba 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -32,14 +32,13 @@ import java.lang.constant.ClassDesc; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.function.Supplier; import jdk.internal.classfile.ClassHierarchyResolver; import static java.lang.constant.ConstantDescs.CD_Object; import static jdk.internal.classfile.Classfile.*; +import static java.util.Objects.requireNonNull; /** * Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface. @@ -52,15 +51,8 @@ public final class ClassHierarchyImpl { static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false); } - public static final ClassHierarchyResolver DEFAULT_RESOLVER = ClassHierarchyResolver - .ofResourceParsing(ResourceParsingClassHierarchyResolver.SYSTEM_STREAM_PROVIDER) - .orElse(new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER)) - .cached(new Supplier<>() { - @Override - public Map get() { - return new ConcurrentHashMap<>(); - } - }); + public static final ClassHierarchyResolver DEFAULT_RESOLVER = + new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER); private final ClassHierarchyResolver resolver; @@ -69,7 +61,10 @@ public final class ClassHierarchyImpl { * @param classHierarchyResolver ClassHierarchyInfoResolver instance */ public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) { - this.resolver = classHierarchyResolver; + requireNonNull(classHierarchyResolver); + this.resolver = classHierarchyResolver instanceof CachedClassHierarchyResolver + ? classHierarchyResolver + : classHierarchyResolver.cached(); } private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java index cdb0b3149e8..455ad35b526 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java @@ -63,9 +63,8 @@ public final class ClassImpl private List> attributes; private List interfaces; - public ClassImpl(byte[] cfbytes, - Collection options) { - this.reader = new ClassReaderImpl(cfbytes, options); + public ClassImpl(byte[] cfbytes, ClassfileImpl context) { + this.reader = new ClassReaderImpl(cfbytes, context); ClassReaderImpl reader = (ClassReaderImpl) this.reader; int p = reader.interfacesPos; int icnt = reader.readU2(p); @@ -94,6 +93,10 @@ public final class ClassImpl reader.setContainedClass(this); } + public int classfileLength() { + return reader.classfileLength(); + } + @Override public AccessFlags flags() { return AccessFlags.ofClass(reader.flags()); @@ -169,20 +172,6 @@ public final class ClassImpl } } - @Override - public byte[] transform(ClassTransform transform) { - ConstantPoolBuilder constantPool = ConstantPoolBuilder.of(this); - return Classfile.build(thisClass(), constantPool, - new Consumer() { - @Override - public void accept(ClassBuilder builder) { - ((DirectClassBuilder) builder).setOriginal(ClassImpl.this); - ((DirectClassBuilder) builder).setSizeHint(reader.classfileLength()); - builder.transform(ClassImpl.this, transform); - } - }); - } - @Override public List fields() { return fields; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java index fe91e674bab..7895b5b3fd8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java @@ -77,7 +77,7 @@ public final class ClassReaderImpl private final int constantPoolCount; private final int[] cpOffset; - final Options options; + final ClassfileImpl context; final int interfacesPos; final PoolEntry[] cp; @@ -86,11 +86,11 @@ public final class ClassReaderImpl private BootstrapMethodsAttribute bootstrapMethodsAttribute; ClassReaderImpl(byte[] classfileBytes, - Collection options) { + ClassfileImpl context) { this.buffer = classfileBytes; this.classfileLength = classfileBytes.length; - this.options = new Options(options); - this.attributeMapper = this.options.attributeMapper; + this.context = context; + this.attributeMapper = this.context.attributeMapperOption().attributeMapper(); if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) { throw new IllegalArgumentException("Bad magic number"); } @@ -134,8 +134,8 @@ public final class ClassReaderImpl this.interfacesPos = p; } - public Options options() { - return options; + public ClassfileImpl context() { + return context; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassfileImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassfileImpl.java new file mode 100644 index 00000000000..6610de76659 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassfileImpl.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.classfile.impl; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Consumer; + +import jdk.internal.classfile.AttributeMapper; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.Classfile.*; +import jdk.internal.classfile.ClassBuilder; +import jdk.internal.classfile.ClassHierarchyResolver; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.ClassTransform; +import jdk.internal.classfile.constantpool.ClassEntry; +import jdk.internal.classfile.constantpool.ConstantPoolBuilder; +import jdk.internal.classfile.constantpool.Utf8Entry; + +public record ClassfileImpl(StackMapsOption stackMapsOption, + DebugElementsOption debugElementsOption, + LineNumbersOption lineNumbersOption, + UnknownAttributesOption unknownAttributesOption, + ConstantPoolSharingOption constantPoolSharingOption, + ShortJumpsOption shortJumpsOption, + DeadCodeOption deadCodeOption, + DeadLabelsOption deadLabelsOption, + ClassHierarchyResolverOption classHierarchyResolverOption, + AttributeMapperOption attributeMapperOption) implements Classfile { + + public static final ClassfileImpl DEFAULT_CONTEXT = new ClassfileImpl( + StackMapsOption.STACK_MAPS_WHEN_REQUIRED, + DebugElementsOption.PASS_DEBUG, + LineNumbersOption.PASS_LINE_NUMBERS, + UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES, + ConstantPoolSharingOption.SHARED_POOL, + ShortJumpsOption.FIX_SHORT_JUMPS, + DeadCodeOption.PATCH_DEAD_CODE, + DeadLabelsOption.FAIL_ON_DEAD_LABELS, + new ClassHierarchyResolverOptionImpl(ClassHierarchyResolver.defaultResolver()), + new AttributeMapperOptionImpl(new Function<>() { + @Override + public AttributeMapper apply(Utf8Entry k) { + return null; + } + })); + + @SuppressWarnings("unchecked") + @Override + public ClassfileImpl withOptions(Option... options) { + var smo = stackMapsOption; + var deo = debugElementsOption; + var lno = lineNumbersOption; + var uao = unknownAttributesOption; + var cpso = constantPoolSharingOption; + var sjo = shortJumpsOption; + var dco = deadCodeOption; + var dlo = deadLabelsOption; + var chro = classHierarchyResolverOption; + var amo = attributeMapperOption; + for (var o : options) { + switch (o) { + case StackMapsOption oo -> smo = oo; + case DebugElementsOption oo -> deo = oo; + case LineNumbersOption oo -> lno = oo; + case UnknownAttributesOption oo -> uao = oo; + case ConstantPoolSharingOption oo -> cpso = oo; + case ShortJumpsOption oo -> sjo = oo; + case DeadCodeOption oo -> dco = oo; + case DeadLabelsOption oo -> dlo = oo; + case ClassHierarchyResolverOption oo -> chro = oo; + case AttributeMapperOption oo -> amo = oo; + } + } + return new ClassfileImpl(smo, deo, lno, uao, cpso, sjo, dco, dlo, chro, amo); + } + + @Override + public ClassModel parse(byte[] bytes) { + return new ClassImpl(bytes, this); + } + + @Override + public byte[] build(ClassEntry thisClassEntry, + ConstantPoolBuilder constantPool, + Consumer handler) { + thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClassEntry); + DirectClassBuilder builder = new DirectClassBuilder((SplitConstantPool)constantPool, this, thisClassEntry); + handler.accept(builder); + return builder.build(); + } + + @Override + public byte[] transform(ClassModel model, ClassEntry newClassName, ClassTransform transform) { + ConstantPoolBuilder constantPool = constantPoolSharingOption() == ConstantPoolSharingOption.SHARED_POOL + ? ConstantPoolBuilder.of(model) + : ConstantPoolBuilder.of(); + return build(newClassName, constantPool, + new Consumer() { + @Override + public void accept(ClassBuilder builder) { + ((DirectClassBuilder) builder).setOriginal((ClassImpl)model); + ((DirectClassBuilder) builder).setSizeHint(((ClassImpl)model).classfileLength()); + builder.transform((ClassImpl)model, transform); + } + }); + } + public record AttributeMapperOptionImpl(Function> attributeMapper) + implements AttributeMapperOption { + } + + public record ClassHierarchyResolverOptionImpl(ClassHierarchyResolver classHierarchyResolver) + implements ClassHierarchyResolverOption { + } +} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 2028098f16b..bc597acf07d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -118,7 +118,7 @@ public final class CodeImpl if (!inflated) { if (labels == null) labels = new LabelImpl[codeLength + 1]; - if (((ClassReaderImpl)classReader).options().processLineNumbers) + if (((ClassReaderImpl)classReader).context().lineNumbersOption() == Classfile.LineNumbersOption.PASS_LINE_NUMBERS) inflateLineNumbers(); inflateJumpTargets(); inflateTypeAnnotations(); @@ -150,6 +150,7 @@ public final class CodeImpl } }, (SplitConstantPool)buf.constantPool(), + ((BufWriterImpl)buf).context(), null).writeTo(buf); } } @@ -166,7 +167,7 @@ public final class CodeImpl inflateMetadata(); boolean doLineNumbers = (lineNumbers != null); generateCatchTargets(consumer); - if (((ClassReaderImpl)classReader).options().processDebug) + if (((ClassReaderImpl)classReader).context().debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG) generateDebugElements(consumer); for (int pos=codeStart; pos handler) { - return withField(new DirectFieldBuilder(constantPool, name, descriptor, null) + return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, null) .run(handler)); } @Override public ClassBuilder transformField(FieldModel field, FieldTransform transform) { - DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, field.fieldName(), + DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(), field.fieldType(), field); builder.transform(field, transform); return withField(builder); @@ -98,13 +99,13 @@ public final class DirectClassBuilder Utf8Entry descriptor, int flags, Consumer handler) { - return withMethod(new DirectMethodBuilder(constantPool, name, descriptor, flags, null) + return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null) .run(handler)); } @Override public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { - DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, method.methodName(), + DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(), method.methodType(), method.flags().flagsMask(), method); @@ -141,7 +142,7 @@ public final class DirectClassBuilder this.flags = flags; } - void setSizeHint(int sizeHint) { + public void setSizeHint(int sizeHint) { this.sizeHint = sizeHint; } @@ -166,8 +167,8 @@ public final class DirectClassBuilder // We maintain two writers, and then we join them at the end int size = sizeHint == 0 ? 256 : sizeHint; - BufWriter head = new BufWriterImpl(constantPool, size); - BufWriterImpl tail = new BufWriterImpl(constantPool, size, thisClassEntry, majorVersion); + BufWriter head = new BufWriterImpl(constantPool, context, size); + BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion); // The tail consists of fields and methods, and attributes // This should trigger all the CP/BSM mutation diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 437d7da4843..97a6ae6b9d4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -41,6 +41,7 @@ import jdk.internal.classfile.Classfile; import jdk.internal.classfile.CodeBuilder; import jdk.internal.classfile.CodeElement; import jdk.internal.classfile.CodeModel; +import jdk.internal.classfile.Instruction; import jdk.internal.classfile.Label; import jdk.internal.classfile.Opcode; import jdk.internal.classfile.TypeKind; @@ -101,14 +102,15 @@ public final class DirectCodeBuilder public static Attribute build(MethodInfo methodInfo, Consumer handler, SplitConstantPool constantPool, + ClassfileImpl context, CodeModel original) { DirectCodeBuilder cb; try { - handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, false)); + handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false)); cb.buildContent(); } catch (LabelOverflowException loe) { - if (constantPool.options().fixJumps) { - handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, true)); + if (context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS) { + handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true)); cb.buildContent(); } else @@ -119,15 +121,16 @@ public final class DirectCodeBuilder private DirectCodeBuilder(MethodInfo methodInfo, SplitConstantPool constantPool, + ClassfileImpl context, CodeModel original, boolean transformFwdJumps) { - super(constantPool); + super(constantPool, context); setOriginal(original); this.methodInfo = methodInfo; this.transformFwdJumps = transformFwdJumps; - this.transformBackJumps = constantPool.options().fixJumps; - bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, cai.codeLength()) - : new BufWriterImpl(constantPool); + this.transformBackJumps = context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS; + bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) + : new BufWriterImpl(constantPool, context); this.startLabel = new LabelImpl(this, 0); this.endLabel = new LabelImpl(this, -1); this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); @@ -196,7 +199,7 @@ public final class DirectCodeBuilder int endPc = labelToBci(h.tryEnd()); int handlerPc = labelToBci(h.handler()); if (startPc == -1 || endPc == -1 || handlerPc == -1) { - if (constantPool.options().filterDeadLabels) { + if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) { handlersSize--; } else { throw new IllegalArgumentException("Unbound label in exception handler"); @@ -220,7 +223,7 @@ public final class DirectCodeBuilder // Backfill branches for which Label didn't have position yet processDeferredLabels(); - if (constantPool.options().processDebug) { + if (context.debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG) { if (!characterRanges.isEmpty()) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.CHARACTER_RANGE_TABLE) { @@ -233,7 +236,7 @@ public final class DirectCodeBuilder var start = labelToBci(cr.startScope()); var end = labelToBci(cr.endScope()); if (start == -1 || end == -1) { - if (constantPool.options().filterDeadLabels) { + if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) { crSize--; } else { throw new IllegalArgumentException("Unbound label in character range"); @@ -262,7 +265,7 @@ public final class DirectCodeBuilder b.writeU2(lvSize); for (LocalVariable l : localVariables) { if (!l.writeTo(b)) { - if (constantPool.options().filterDeadLabels) { + if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) { lvSize--; } else { throw new IllegalArgumentException("Unbound label in local variable type"); @@ -285,7 +288,7 @@ public final class DirectCodeBuilder b.writeU2(localVariableTypes.size()); for (LocalVariableType l : localVariableTypes) { if (!l.writeTo(b)) { - if (constantPool.options().filterDeadLabels) { + if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) { lvtSize--; } else { throw new IllegalArgumentException("Unbound label in local variable type"); @@ -305,6 +308,44 @@ public final class DirectCodeBuilder } content = new UnboundAttribute.AdHocAttribute<>(Attributes.CODE) { + + private void writeCounters(boolean codeMatch, BufWriterImpl buf) { + if (codeMatch) { + buf.writeU2(original.maxStack()); + buf.writeU2(original.maxLocals()); + } else { + StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); + buf.writeU2(cntr.maxStack()); + buf.writeU2(cntr.maxLocals()); + } + } + + private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException { + //new instance of generator immediately calculates maxStack, maxLocals, all frames, + // patches dead bytecode blocks and removes them from exception table + StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf); + attributes.withAttribute(gen.stackMapTableAttribute()); + buf.writeU2(gen.maxStack()); + buf.writeU2(gen.maxLocals()); + } + + private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { + if (buf.getMajorVersion() >= Classfile.JAVA_6_VERSION) { + try { + generateStackMaps(buf); + } catch (IllegalArgumentException e) { + //failover following JVMS-4.10 + if (buf.getMajorVersion() == Classfile.JAVA_6_VERSION) { + writeCounters(codeMatch, buf); + } else { + throw e; + } + } + } else { + writeCounters(codeMatch, buf); + } + } + @Override public void writeBody(BufWriter b) { BufWriterImpl buf = (BufWriterImpl) b; @@ -318,52 +359,29 @@ public final class DirectCodeBuilder methodInfo.methodName().stringValue(), methodInfo.methodTypeSymbol().displayDescriptor())); } - int maxStack, maxLocals; - Attribute stackMapAttr; - boolean canReuseStackmaps = codeAndExceptionsMatch(codeLength); - if (!constantPool.options().generateStackmaps) { - StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - maxStack = cntr.maxStack(); - maxLocals = cntr.maxLocals(); - stackMapAttr = null; - } - else if (canReuseStackmaps) { - maxLocals = original.maxLocals(); - maxStack = original.maxStack(); - stackMapAttr = original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null); - } - else if (buf.getMajorVersion() >= Classfile.JAVA_6_VERSION) { - try { - //new instance of generator immediately calculates maxStack, maxLocals, all frames, - // patches dead bytecode blocks and removes them from exception table - StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf); - maxStack = gen.maxStack(); - maxLocals = gen.maxLocals(); - stackMapAttr = gen.stackMapTableAttribute(); - } catch (IllegalArgumentException e) { - if (buf.getMajorVersion() == Classfile.JAVA_6_VERSION) { - //failover following JVMS-4.10 - StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - maxStack = cntr.maxStack(); - maxLocals = cntr.maxLocals(); - stackMapAttr = null; - } else { - throw e; + if (codeAndExceptionsMatch(codeLength)) { + switch (context.stackMapsOption()) { + case STACK_MAPS_WHEN_REQUIRED -> { + attributes.withAttribute(original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null)); + writeCounters(true, buf); } + case GENERATE_STACK_MAPS -> + generateStackMaps(buf); + case DROP_STACK_MAPS -> + writeCounters(true, buf); + } + } else { + switch (context.stackMapsOption()) { + case STACK_MAPS_WHEN_REQUIRED -> + tryGenerateStackMaps(false, buf); + case GENERATE_STACK_MAPS -> + generateStackMaps(buf); + case DROP_STACK_MAPS -> + writeCounters(false, buf); } } - else { - StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - maxStack = cntr.maxStack(); - maxLocals = cntr.maxLocals(); - stackMapAttr = null; - } - attributes.withAttribute(stackMapAttr); - - buf.writeU2(maxStack); - buf.writeU2(maxLocals); buf.writeInt(codeLength); buf.writeBytes(bytecodesBufWriter); writeExceptionHandlers(b); @@ -377,9 +395,9 @@ public final class DirectCodeBuilder private final BufWriterImpl buf; private int lastPc, lastLine, writtenLine; - public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool) { + public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassfileImpl context) { super(Attributes.LINE_NUMBER_TABLE); - buf = new BufWriterImpl(constantPool); + buf = new BufWriterImpl(constantPool, context); lastPc = -1; writtenLine = -1; } @@ -424,7 +442,7 @@ public final class DirectCodeBuilder codeAttributesMatch = cai.codeLength == curPc() && cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength); if (codeAttributesMatch) { - BufWriter bw = new BufWriterImpl(constantPool); + BufWriter bw = new BufWriterImpl(constantPool, context); writeExceptionHandlers(bw); codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size()); } @@ -682,7 +700,7 @@ public final class DirectCodeBuilder public void setLineNumber(int lineNo) { if (lineNumberWriter == null) - lineNumberWriter = new DedupLineNumberTableAttribute(constantPool); + lineNumberWriter = new DedupLineNumberTableAttribute(constantPool, context); lineNumberWriter.writeLineNumber(curPc(), lineNo); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java index 9edcdc9c0b6..e0e8af5e20a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java @@ -42,10 +42,11 @@ public final class DirectFieldBuilder private int flags; public DirectFieldBuilder(SplitConstantPool constantPool, + ClassfileImpl context, Utf8Entry name, Utf8Entry type, FieldModel original) { - super(constantPool); + super(constantPool, context); setOriginal(original); this.name = name; this.desc = type; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index 84be7429d8a..9e0005fd0c8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -50,11 +50,12 @@ public final class DirectMethodBuilder MethodTypeDesc mDesc; public DirectMethodBuilder(SplitConstantPool constantPool, + ClassfileImpl context, Utf8Entry nameInfo, Utf8Entry typeInfo, int flags, MethodModel original) { - super(constantPool); + super(constantPool, context); setOriginal(original); this.name = nameInfo; this.desc = typeInfo; @@ -105,7 +106,7 @@ public final class DirectMethodBuilder @Override public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) { - return new BufferedCodeBuilder(this, constantPool, original); + return new BufferedCodeBuilder(this, constantPool, context, original); } @Override @@ -116,7 +117,7 @@ public final class DirectMethodBuilder private MethodBuilder withCode(CodeModel original, Consumer handler) { - var cb = DirectCodeBuilder.build(this, handler, constantPool, original); + var cb = DirectCodeBuilder.build(this, handler, constantPool, context, original); writeAttribute(cb); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Options.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Options.java deleted file mode 100644 index 1f017625f2d..00000000000 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Options.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.classfile.impl; - -import java.util.Collection; -import java.util.function.Function; - -import jdk.internal.classfile.AttributeMapper; -import jdk.internal.classfile.ClassHierarchyResolver; -import jdk.internal.classfile.Classfile; -import jdk.internal.classfile.constantpool.Utf8Entry; - -import static jdk.internal.classfile.ClassHierarchyResolver.defaultResolver; - -public class Options { - - public enum Key { - GENERATE_STACK_MAPS, PROCESS_DEBUG, PROCESS_LINE_NUMBERS, PROCESS_UNKNOWN_ATTRIBUTES, - CP_SHARING, FIX_SHORT_JUMPS, PATCH_DEAD_CODE, HIERARCHY_RESOLVER, ATTRIBUTE_MAPPER, - FILTER_DEAD_LABELS; - } - - public record OptionValue(Key key, Object value) implements Classfile.Option { } - - public Boolean generateStackmaps = true; - public Boolean processDebug = true; - public Boolean processLineNumbers = true; - public Boolean processUnknownAttributes = true; - public Boolean cpSharing = true; - public Boolean fixJumps = true; - public Boolean patchCode = true; - public Boolean filterDeadLabels = false; - public ClassHierarchyResolver classHierarchyResolver = defaultResolver(); - public Function> attributeMapper = new Function<>() { - @Override - public AttributeMapper apply(Utf8Entry k) { - return null; - } - }; - - @SuppressWarnings("unchecked") - public Options(Collection options) { - for (var o : options) { - var ov = ((OptionValue)o); - var v = ov.value(); - switch (ov.key()) { - case GENERATE_STACK_MAPS -> generateStackmaps = (Boolean) v; - case PROCESS_DEBUG -> processDebug = (Boolean) v; - case PROCESS_LINE_NUMBERS -> processLineNumbers = (Boolean) v; - case PROCESS_UNKNOWN_ATTRIBUTES -> processUnknownAttributes = (Boolean) v; - case CP_SHARING -> cpSharing = (Boolean) v; - case FIX_SHORT_JUMPS -> fixJumps = (Boolean) v; - case PATCH_DEAD_CODE -> patchCode = (Boolean) v; - case HIERARCHY_RESOLVER -> classHierarchyResolver = (ClassHierarchyResolver) v; - case ATTRIBUTE_MAPPER -> attributeMapper = (Function>) v; - case FILTER_DEAD_LABELS -> filterDeadLabels = (Boolean) v; - } - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 2e74e01e54d..367c7f74762 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -27,7 +27,6 @@ package jdk.internal.classfile.impl; import java.lang.constant.ConstantDesc; import java.lang.constant.MethodTypeDesc; import java.util.Arrays; -import java.util.Collections; import java.util.List; import jdk.internal.classfile.Attribute; @@ -81,7 +80,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder { private final ClassReaderImpl parent; private final int parentSize, parentBsmSize; - final Options options; private int size, bsmSize; private PoolEntry[] myEntries; @@ -91,10 +89,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder { private EntryMap bsmMap; public SplitConstantPool() { - this(new Options(Collections.emptyList())); - } - - public SplitConstantPool(Options options) { this.size = 1; this.bsmSize = 0; this.myEntries = new PoolEntry[1024]; @@ -102,12 +96,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder { this.parent = null; this.parentSize = 0; this.parentBsmSize = 0; - this.options = options; this.doneFullScan = true; } public SplitConstantPool(ClassReader parent) { - this.options = ((ClassReaderImpl) parent).options; this.parent = (ClassReaderImpl) parent; this.parentSize = parent.entryCount(); this.parentBsmSize = parent.bootstrapMethodCount(); @@ -117,18 +109,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder { this.myBsmEntries = new BootstrapMethodEntryImpl[8]; } - //clone constructor for internal purposes - SplitConstantPool(SplitConstantPool cloneFrom, Options options) { - this.options = options; - this.parent = cloneFrom.parent; - this.parentSize = cloneFrom.parentSize; - this.parentBsmSize = cloneFrom.parentBsmSize; - this.size = cloneFrom.size; - this.bsmSize = cloneFrom.bsmSize; - this.myEntries = Arrays.copyOf(cloneFrom.myEntries, cloneFrom.myEntries.length); - this.myBsmEntries = Arrays.copyOf(cloneFrom.myBsmEntries, cloneFrom.myBsmEntries.length); - } - @Override public int entryCount() { return size; @@ -153,10 +133,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder { : myBsmEntries[index - parentBsmSize]; } - public Options options() { - return options; - } - @Override public boolean canWriteDirect(ConstantPool other) { return this == other || parent == other; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 575808d13b0..de85aeae515 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -153,6 +153,7 @@ public final class StackMapGenerator { (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()), dcb.constantPool, + dcb.context, dcb.handlers); } @@ -194,6 +195,7 @@ public final class StackMapGenerator { private final List rawHandlers; private final ClassHierarchyImpl classHierarchy; private final boolean patchDeadCode; + private final boolean filterDeadLabels; private List frames; private final Frame currentFrame; private int maxStack, maxLocals; @@ -221,6 +223,7 @@ public final class StackMapGenerator { boolean isStatic, ByteBuffer bytecode, SplitConstantPool cp, + ClassfileImpl context, List handlers) { this.thisType = Type.referenceType(thisClass); this.methodName = methodName; @@ -231,8 +234,9 @@ public final class StackMapGenerator { this.labelContext = labelContext; this.handlers = handlers; this.rawHandlers = new ArrayList<>(handlers.size()); - this.classHierarchy = new ClassHierarchyImpl(cp.options().classHierarchyResolver); - this.patchDeadCode = cp.options().patchCode; + this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolverOption().classHierarchyResolver()); + this.patchDeadCode = context.deadCodeOption() == Classfile.DeadCodeOption.PATCH_DEAD_CODE; + this.filterDeadLabels = context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS; this.currentFrame = new Frame(classHierarchy); generate(); } @@ -832,22 +836,21 @@ public final class StackMapGenerator { methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")))); //try to attach debug info about corrupted bytecode to the message try { - //clone SplitConstantPool with alternate Options - var newCp = new SplitConstantPool(cp, new Options(List.of(Classfile.Option.generateStackmap(false)))); - var clb = new DirectClassBuilder(newCp, newCp.classEntry(ClassDesc.of("FakeClass"))); - clb.withMethod(methodName, methodDesc, isStatic ? ACC_STATIC : 0, mb -> - ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.CODE) { - @Override - public void writeBody(BufWriter b) { - b.writeU2(-1);//max stack - b.writeU2(-1);//max locals - b.writeInt(bytecode.limit()); - b.writeBytes(bytecode.array(), 0, bytecode.limit()); - b.writeU2(0);//exception handlers - b.writeU2(0);//attributes - } - })); - ClassPrinter.toYaml(Classfile.parse(clb.build()).methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append); + var cc = Classfile.of(); + var clm = cc.parse(cc.build(cp.classEntry(thisType.sym()), cp, clb -> + clb.withMethod(methodName, methodDesc, isStatic ? ACC_STATIC : 0, mb -> + ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.CODE) { + @Override + public void writeBody(BufWriter b) { + b.writeU2(-1);//max stack + b.writeU2(-1);//max locals + b.writeInt(bytecode.limit()); + b.writeBytes(bytecode.array(), 0, bytecode.limit()); + b.writeU2(0);//exception handlers + b.writeU2(0);//attributes + } + })))); + ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append); } catch (Error | Exception suppresed) { //fallback to bytecode hex dump bytecode.rewind(); @@ -931,7 +934,7 @@ public final class StackMapGenerator { for (var exhandler : rawHandlers) try { offsets.set(exhandler.handler()); } catch (IllegalArgumentException iae) { - if (!cp.options().filterDeadLabels) + if (!filterDeadLabels) generatorError("Detected exception handler out of bytecode range"); } return offsets; diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/CharacterRange.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/CharacterRange.java index 36132741179..7cbf5433773 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/CharacterRange.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/CharacterRange.java @@ -37,7 +37,7 @@ import jdk.internal.classfile.impl.BoundCharacterRange; * A pseudo-instruction which models a single entry in the * {@link CharacterRangeTableAttribute}. Delivered as a {@link CodeElement} * during traversal of the elements of a {@link CodeModel}, according to - * the setting of the {@link Classfile.Option#processDebug(boolean)} option. + * the setting of the {@link Classfile.DebugElementsOption} option. */ public sealed interface CharacterRange extends PseudoInstruction permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange { diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/ConstantInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/ConstantInstruction.java index 031aee61b1c..38de93f0769 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/ConstantInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/ConstantInstruction.java @@ -136,7 +136,7 @@ public sealed interface ConstantInstruction extends Instruction { static ArgumentConstantInstruction ofArgument(Opcode op, int value) { Util.checkKind(op, Opcode.Kind.CONSTANT); if (op != Opcode.BIPUSH && op != Opcode.SIPUSH) - throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op, op.kind())); + throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op)); return new AbstractInstruction.UnboundArgumentConstantInstruction(op, value); } @@ -150,7 +150,7 @@ public sealed interface ConstantInstruction extends Instruction { static LoadConstantInstruction ofLoad(Opcode op, LoadableConstantEntry constant) { Util.checkKind(op, Opcode.Kind.CONSTANT); if (op != Opcode.LDC && op != Opcode.LDC_W && op != Opcode.LDC2_W) - throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op, op.kind())); + throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op)); return new AbstractInstruction.UnboundLoadConstantInstruction(op, constant); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/LineNumber.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/LineNumber.java index 36d7735d13c..ae9c0271ef6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/LineNumber.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/LineNumber.java @@ -35,7 +35,7 @@ import jdk.internal.classfile.impl.LineNumberImpl; * A pseudo-instruction which models a single entry in the * {@link LineNumberTableAttribute}. Delivered as a {@link CodeElement} * during traversal of the elements of a {@link CodeModel}, according to - * the setting of the {@link Classfile.Option#processLineNumbers(boolean)} option. + * the setting of the {@link Classfile.LineNumbersOption} option. * * @see PseudoInstruction */ diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariable.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariable.java index e60acac19c2..ac803e3cf3c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariable.java @@ -42,7 +42,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool; * A pseudo-instruction which models a single entry in the * {@link LocalVariableTableAttribute}. Delivered as a {@link CodeElement} * during traversal of the elements of a {@link CodeModel}, according to - * the setting of the {@link Classfile.Option#processDebug(boolean)} option. + * the setting of the {@link Classfile.DebugElementsOption} option. * * @see PseudoInstruction */ diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariableType.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariableType.java index 9093e777304..3c01f1df3d4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariableType.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/LocalVariableType.java @@ -41,7 +41,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool; * A pseudo-instruction which models a single entry in the {@link * LocalVariableTypeTableAttribute}. Delivered as a {@link CodeElement} during * traversal of the elements of a {@link CodeModel}, according to the setting of - * the {@link Classfile.Option#processDebug(boolean)} option. + * the {@link Classfile.DebugElementsOption} option. */ public sealed interface LocalVariableType extends PseudoInstruction permits AbstractPseudoInstruction.UnboundLocalVariableType, BoundLocalVariableType { diff --git a/src/java.base/share/classes/jdk/internal/classfile/package-info.java b/src/java.base/share/classes/jdk/internal/classfile/package-info.java index 5cd82c9c166..1286f791c61 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/package-info.java +++ b/src/java.base/share/classes/jdk/internal/classfile/package-info.java @@ -33,10 +33,10 @@ *

Reading classfiles

* The main class for reading classfiles is {@link jdk.internal.classfile.ClassModel}; we * convert bytes into a {@link jdk.internal.classfile.ClassModel} with {@link - * jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])}: + * jdk.internal.classfile.Classfile#parse(byte[])}: *

* {@snippet lang=java : - * ClassModel cm = Classfile.parse(bytes); + * ClassModel cm = Classfile.of().parse(bytes); * } *

* There are several additional overloads of {@code parse} that let you specify @@ -164,31 +164,30 @@ *

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

Options

*

- * {@link jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])} - * accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} exports some - * static boolean options, as well as factories for more complex options, + * {@link jdk.internal.classfile.Classfile#of(jdk.internal.classfile.Classfile.Option[])} + * accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} is a base interface + * for some statically enumerated options, as well as factories for more complex options, * including: *

*

@@ -304,7 +303,8 @@ *

* and then transform the classfile: * {@snippet lang=java : - * byte[] newBytes = Classfile.parse(bytes).transform(ct); + * var cc = Classfile.of(); + * byte[] newBytes = cc.transform(cc.parse(bytes), ct); * } *

* This is much more concise (and less error-prone) than the equivalent @@ -322,10 +322,11 @@ * jdk.internal.classfile.CodeTransform#andThen(jdk.internal.classfile.CodeTransform)}: *

* {@snippet lang=java : - * byte[] newBytes = Classfile.parse(bytes) - * .transform(ClassTransform.transformingMethods( - * MethodTransform.transformingCode( - * fooToBar.andThen(instrumentCalls)))); + * var cc = Classfile.of(); + * byte[] newBytes = cc.transform(cc.parse(bytes), + * ClassTransform.transformingMethods( + * MethodTransform.transformingCode( + * fooToBar.andThen(instrumentCalls)))); * } * * Transform {@code instrumentCalls} will receive all code elements produced by @@ -341,7 +342,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 jdk.internal.classfile.Classfile.Option#constantPoolSharing(boolean)} option. + * with the {@link jdk.internal.classfile.Classfile.ConstantPoolSharingOption} option. * Such suppression may be beneficial when transformation removes many elements, * resulting in many unreferenced constant pool entries. * diff --git a/src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java b/src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java index 931fe5e6c7b..2d6a13c34fa 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java +++ b/src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.Set; import java.lang.reflect.AccessFlag; +import java.util.ArrayDeque; import java.util.LinkedList; import java.util.Map; import java.util.function.Predicate; @@ -62,7 +63,7 @@ import jdk.internal.classfile.instruction.StoreInstruction; class PackageSnippets { void enumerateFieldsMethods1(byte[] bytes) { // @start region="enumerateFieldsMethods1" - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = Classfile.of().parse(bytes); for (FieldModel fm : cm.fields()) System.out.printf("Field %s%n", fm.fieldName().stringValue()); for (MethodModel mm : cm.methods()) @@ -72,7 +73,7 @@ class PackageSnippets { void enumerateFieldsMethods2(byte[] bytes) { // @start region="enumerateFieldsMethods2" - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = Classfile.of().parse(bytes); for (ClassElement ce : cm) { switch (ce) { case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue()); @@ -85,7 +86,7 @@ class PackageSnippets { void gatherDependencies1(byte[] bytes) { // @start region="gatherDependencies1" - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = Classfile.of().parse(bytes); Set dependencies = new HashSet<>(); for (ClassElement ce : cm) { @@ -108,7 +109,7 @@ class PackageSnippets { void gatherDependencies2(byte[] bytes) { // @start region="gatherDependencies2" - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = Classfile.of().parse(bytes); Set dependencies = cm.elementStream() .flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty()) @@ -126,7 +127,7 @@ class PackageSnippets { void writeHelloWorld() { // @start region="helloWorld" - byte[] bytes = Classfile.build(ClassDesc.of("Hello"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Hello"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("", MethodTypeDesc.of(ConstantDescs.CD_void), Classfile.ACC_PUBLIC, mb -> mb.withCode( @@ -152,8 +153,8 @@ class PackageSnippets { void stripDebugMethods1(byte[] bytes) { // @start region="stripDebugMethods1" - ClassModel classModel = Classfile.parse(bytes); - byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(), + ClassModel classModel = Classfile.of().parse(bytes); + byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(), classBuilder -> { for (ClassElement ce : classModel) { if (!(ce instanceof MethodModel mm @@ -170,7 +171,8 @@ class PackageSnippets { if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug"))) builder.with(element); }; - byte[] newBytes = Classfile.parse(bytes).transform(ct); + var cc = Classfile.of(); + byte[] newBytes = cc.transform(cc.parse(bytes), ct); // @end } @@ -202,7 +204,7 @@ class PackageSnippets { void fooToBarUnrolled(ClassModel classModel) { // @start region="fooToBarUnrolled" - byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(), + byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(), classBuilder -> { for (ClassElement ce : classModel) { if (ce instanceof MethodModel mm) { @@ -234,7 +236,7 @@ class PackageSnippets { void codeRelabeling(ClassModel classModel) { // @start region="codeRelabeling" - byte[] newBytes = classModel.transform( + byte[] newBytes = Classfile.of().transform(classModel, ClassTransform.transformingMethodBodies( CodeTransform.ofStateful(CodeRelabeler::of))); // @end @@ -244,11 +246,11 @@ class PackageSnippets { byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate instrumentedMethodsFilter) { var instrumentorCodeMap = instrumentor.methods().stream() .filter(instrumentedMethodsFilter) - .collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElse(null))); + .collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElseThrow())); var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); - return target.transform( + return Classfile.of().transform(target, ClassTransform.transformingMethods( instrumentedMethodsFilter, (mb, me) -> { @@ -266,13 +268,13 @@ class PackageSnippets { && mm.methodType().stringValue().equals(inv.type().stringValue())) { //store stacked method parameters into locals - var storeStack = new LinkedList(); + var storeStack = new ArrayDeque(); int slot = 0; if (!mm.flags().has(AccessFlag.STATIC)) - storeStack.add(StoreInstruction.of(TypeKind.ReferenceType, slot++)); + storeStack.push(StoreInstruction.of(TypeKind.ReferenceType, slot++)); for (var pt : mm.methodTypeSymbol().parameterList()) { var tk = TypeKind.from(pt); - storeStack.addFirst(StoreInstruction.of(tk, slot)); + storeStack.push(StoreInstruction.of(tk, slot)); slot += tk.slotSize(); } storeStack.forEach(codeBuilder::with); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java index 6e6df996973..b447bd31b48 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java @@ -186,7 +186,7 @@ public class BindingSpecializer { private static byte[] specializeHelper(MethodType leafType, MethodType callerMethodType, CallingSequence callingSequence, ABIDescriptor abi) { String className = callingSequence.forDowncall() ? CLASS_NAME_DOWNCALL : CLASS_NAME_UPCALL; - byte[] bytes = Classfile.build(ClassDesc.ofInternalName(className), clb -> { + byte[] bytes = Classfile.of().build(ClassDesc.ofInternalName(className), clb -> { clb.withFlags(ACC_PUBLIC + ACC_FINAL + ACC_SUPER); clb.withSuperclass(CD_Object); clb.withVersion(CLASSFILE_VERSION, 0); @@ -207,7 +207,7 @@ public class BindingSpecializer { } if (PERFORM_VERIFICATION) { - List errors = Classfile.parse(bytes).verify(null); + List errors = Classfile.of().parse(bytes).verify(null); if (!errors.isEmpty()) { errors.forEach(System.err::println); throw new IllegalStateException("Verification error(s)"); diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java index 9992b4bf396..e5b675fc776 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java @@ -150,9 +150,10 @@ public final class ModuleInfoExtender { * be discarded. */ public byte[] toByteArray() throws IOException { - var cm = Classfile.parse(in.readAllBytes()); + var cc = Classfile.of(); + var cm = cc.parse(in.readAllBytes()); Version v = ModuleInfoExtender.this.version; - return cm.transform(ClassTransform.endHandler(clb -> { + return cc.transform(cm, ClassTransform.endHandler(clb -> { // ModuleMainClass attribute if (mainClass != null) { clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass))); diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java b/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java index fb507d9846b..880520ff8cf 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java @@ -166,7 +166,7 @@ final class FingerPrint { } private static ClassAttributes getClassAttributes(byte[] bytes) { - var cm = Classfile.parse(bytes); + var cm = Classfile.of().parse(bytes); ClassAttributes attrs = new ClassAttributes( cm.flags(), cm.thisClass().asInternalName(), diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java index 731a1ffedb5..3f9936e5112 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java @@ -368,7 +368,7 @@ class JImageTask { if (name.endsWith(".class") && !name.endsWith("module-info.class")) { try { byte[] bytes = reader.getResource(location); - Classfile.parse(bytes).forEachElement(cle -> { + Classfile.of().parse(bytes).forEachElement(cle -> { if (cle instanceof MethodModel mm) mm.forEachElement(me -> { if (me instanceof CodeModel com) com.forEachElement(coe -> { //do nothing here, just visit each model element diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AbstractPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AbstractPlugin.java index 0fdc1604028..7d8d9c1e34f 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AbstractPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AbstractPlugin.java @@ -88,7 +88,7 @@ public abstract class AbstractPlugin implements Plugin { ClassModel newClassReader(String path, ResourcePoolEntry resource, Classfile.Option... options) { byte[] content = resource.contentBytes(); try { - return Classfile.parse(content, options); + return Classfile.of(options).parse(content); } catch (Exception e) { if (JlinkTask.DEBUG) { System.err.printf("Failed to parse class file: %s from resource of type %s\n", path, @@ -102,7 +102,7 @@ public abstract class AbstractPlugin implements Plugin { protected ClassModel newClassReader(String path, byte[] buf, Classfile.Option... options) { try { - return Classfile.parse(buf, options); + return Classfile.of(options).parse(buf); } catch (Exception e) { if (JlinkTask.DEBUG) { System.err.printf("Failed to parse class file: %s\n", path); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java index e9d9a5f6e37..d1ce2c414ae 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java @@ -64,15 +64,16 @@ public final class StripJavaDebugAttributesPlugin extends AbstractPlugin { if (path.endsWith("module-info.class")) { // XXX. Do we have debug info? } else { - byte[] content = newClassReader(path, resource, - Classfile.Option.processDebug(false), - Classfile.Option.processLineNumbers(false)).transform(ClassTransform + var clm = newClassReader(path, resource, + Classfile.DebugElementsOption.DROP_DEBUG, + Classfile.LineNumbersOption.DROP_LINE_NUMBERS); + byte[] content = Classfile.of().transform(clm, ClassTransform .dropping(cle -> cle instanceof SourceFileAttribute - || cle instanceof SourceDebugExtensionAttribute) - .andThen(ClassTransform.transformingMethods(MethodTransform - .dropping(me -> me instanceof MethodParametersAttribute) - .andThen(MethodTransform - .transformingCode(CodeTransform.ACCEPT_ALL))))); + || cle instanceof SourceDebugExtensionAttribute) + .andThen(ClassTransform.transformingMethods(MethodTransform + .dropping(me -> me instanceof MethodParametersAttribute) + .andThen(MethodTransform + .transformingCode(CodeTransform.ACCEPT_ALL))))); res = resource.copyWithContent(content); } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java index c01042bdc94..dcd029a607a 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java @@ -431,7 +431,7 @@ public final class SystemModulesPlugin extends AbstractPlugin { boolean hasModulePackages() throws IOException { try (InputStream in = getInputStream()) { // parse module-info.class - return Classfile.parse(in.readAllBytes()).elementStream() + return Classfile.of().parse(in.readAllBytes()).elementStream() .anyMatch(e -> e instanceof ModulePackagesAttribute mpa && !mpa.packages().isEmpty()); } @@ -579,7 +579,7 @@ public final class SystemModulesPlugin extends AbstractPlugin { * Generate SystemModules class */ public byte[] genClassBytes(Configuration cf) { - return Classfile.build(classDesc, + return Classfile.of().build(classDesc, clb -> { clb.withFlags(ACC_FINAL + ACC_SUPER) .withInterfaceSymbols(List.of(CD_SYSTEM_MODULES)) @@ -1676,7 +1676,7 @@ public final class SystemModulesPlugin extends AbstractPlugin { // write the class file to the pool as a resource String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASSNAME + ".class"; - ResourcePoolEntry e = ResourcePoolEntry.create(rn, Classfile.build( + ResourcePoolEntry e = ResourcePoolEntry.create(rn, Classfile.of().build( CD_SYSTEM_MODULES_MAP, clb -> clb.withFlags(ACC_FINAL + ACC_SUPER) .withVersion(52, 0) diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VersionPropsPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VersionPropsPlugin.java index bdc79daf394..455cbf71869 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VersionPropsPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VersionPropsPlugin.java @@ -26,6 +26,7 @@ package jdk.tools.jlink.internal.plugins; import java.util.Map; +import jdk.internal.classfile.Classfile; import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.CodeBuilder; import jdk.internal.classfile.CodeElement; @@ -99,7 +100,8 @@ abstract class VersionPropsPlugin extends AbstractPlugin { @SuppressWarnings("deprecation") private byte[] redefine(String path, byte[] classFile) { - return newClassReader(path, classFile).transform(ClassTransform.transformingMethodBodies( + return Classfile.of().transform(newClassReader(path, classFile), + ClassTransform.transformingMethodBodies( mm -> mm.methodName().equalsString(""), new CodeTransform() { private CodeElement pendingLDC = null; diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java index ca2282341c0..39c7b78eee7 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java @@ -80,8 +80,9 @@ public class LocalExecutionControl extends DirectExecutionControl { private static final MethodTypeDesc MTD_void = MethodTypeDesc.of(ConstantDescs.CD_void); private static byte[] instrument(byte[] classFile) { - return Classfile.parse(classFile) - .transform(ClassTransform.transformingMethodBodies((cob, coe) -> { + var cc = Classfile.of(); + return cc.transform(cc.parse(classFile), + ClassTransform.transformingMethodBodies((cob, coe) -> { if (coe instanceof BranchInstruction) cob.invokestatic(CD_Cancel, "stopCheck", MTD_void); cob.with(coe); @@ -89,7 +90,7 @@ public class LocalExecutionControl extends DirectExecutionControl { } private static ClassBytecodes genCancelClass() { - return new ClassBytecodes(CANCEL_CLASS, Classfile.build(CD_Cancel, clb -> + return new ClassBytecodes(CANCEL_CLASS, Classfile.of().build(CD_Cancel, clb -> clb.withFlags(Classfile.ACC_PUBLIC) .withField("allStop", ConstantDescs.CD_boolean, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC | Classfile.ACC_VOLATILE) .withMethodBody("stopCheck", MTD_void, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC, cob -> diff --git a/test/jdk/java/lang/module/ModuleDescriptorTest.java b/test/jdk/java/lang/module/ModuleDescriptorTest.java index 17ce09f6c0c..f0155488242 100644 --- a/test/jdk/java/lang/module/ModuleDescriptorTest.java +++ b/test/jdk/java/lang/module/ModuleDescriptorTest.java @@ -1371,7 +1371,7 @@ public class ModuleDescriptorTest { * complete set of packages. */ public void testReadsWithBadPackageFinder() throws Exception { - ByteBuffer bb = ByteBuffer.wrap(Classfile.buildModule( + ByteBuffer bb = ByteBuffer.wrap(Classfile.of().buildModule( ModuleAttribute.of( ModuleDesc.of("foo"), mb -> mb.requires(ModuleDesc.of("java.base"), 0, null) diff --git a/test/jdk/jdk/classfile/AdaptCodeTest.java b/test/jdk/jdk/classfile/AdaptCodeTest.java index 309e5bb969a..5fd0c8601c3 100644 --- a/test/jdk/jdk/classfile/AdaptCodeTest.java +++ b/test/jdk/jdk/classfile/AdaptCodeTest.java @@ -58,9 +58,10 @@ class AdaptCodeTest { @Test void testNullAdaptIterator() throws Exception { - ClassModel cm = Classfile.parse(testClassPath); + var cc = Classfile.of(); + ClassModel cm = cc.parse(testClassPath); for (ClassTransform t : Transforms.noops) { - byte[] newBytes = cm.transform(t); + byte[] newBytes = cc.transform(cm, t); String result = (String) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) .getMethod(testClassName, "many") @@ -77,15 +78,17 @@ class AdaptCodeTest { }) void testNullAdaptIterator2(String path) throws Exception { FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); - ClassModel cm = Classfile.parse(fs.getPath(path)); + var cc = Classfile.of(); + ClassModel cm = cc.parse(fs.getPath(path)); for (ClassTransform t : Transforms.noops) { - byte[] newBytes = cm.transform(t); + byte[] newBytes = cc.transform(cm, t); } } @Test void testSevenOfThirteenIterator() throws Exception { - ClassModel cm = Classfile.parse(testClassPath); + var cc = Classfile.of(); + ClassModel cm = cc.parse(testClassPath); var transform = ClassTransform.transformingMethodBodies((codeB, codeE) -> { switch (codeE) { @@ -100,7 +103,7 @@ class AdaptCodeTest { } }); - byte[] newBytes = cm.transform(transform); + byte[] newBytes = cc.transform(cm, transform); // Files.write(Path.of("foo.class"), newBytes); String result = (String) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) @@ -111,8 +114,9 @@ class AdaptCodeTest { @Test void testCopy() throws Exception { - ClassModel cm = Classfile.parse(testClassPath); - byte[] newBytes = Classfile.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb)); + var cc = Classfile.of(); + ClassModel cm = cc.parse(testClassPath); + byte[] newBytes = cc.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb)); // TestUtil.writeClass(newBytes, "TestClass.class"); String result = (String) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) diff --git a/test/jdk/jdk/classfile/AdvancedTransformationsTest.java b/test/jdk/jdk/classfile/AdvancedTransformationsTest.java index e9403b75ac0..0c684b757cc 100644 --- a/test/jdk/jdk/classfile/AdvancedTransformationsTest.java +++ b/test/jdk/jdk/classfile/AdvancedTransformationsTest.java @@ -75,8 +75,9 @@ class AdvancedTransformationsTest { @Test void testShiftLocals() throws Exception { try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) { - var clm = Classfile.parse(in.readAllBytes()); - var remapped = Classfile.parse(clm.transform((clb, cle) -> { + var cc = Classfile.of(); + var clm = cc.parse(in.readAllBytes()); + var remapped = cc.parse(cc.transform(clm, (clb, cle) -> { if (cle instanceof MethodModel mm) { clb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel com) { @@ -113,8 +114,9 @@ class AdvancedTransformationsTest { ClassDesc.ofDescriptor(StackMapGenerator.class.descriptorString()), ClassDesc.of("remapped.StackMapGenerator") ); try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) { - var clm = Classfile.parse(in.readAllBytes()); - var remapped = Classfile.parse(ClassRemapper.of(map).remapClass(clm)); + var cc = Classfile.of(); + var clm = cc.parse(in.readAllBytes()); + var remapped = cc.parse(ClassRemapper.of(map).remapClass(cc, clm)); assertEmpty(remapped.verify( ClassHierarchyResolver.of(Set.of(ClassDesc.of("remapped.List")), Map.of( ClassDesc.of("remapped.RemappedBytecode"), ConstantDescs.CD_Object, @@ -167,11 +169,12 @@ class AdvancedTransformationsTest { void testRemapModule() throws Exception { var foo = ClassDesc.ofDescriptor(Foo.class.descriptorString()); var bar = ClassDesc.ofDescriptor(Bar.class.descriptorString()); - - var ma = Classfile.parse( + var cc = Classfile.of(); + var ma = cc.parse( ClassRemapper.of(Map.of(foo, bar)).remapClass( - Classfile.parse( - Classfile.buildModule( + cc, + cc.parse( + cc.buildModule( ModuleAttribute.of(ModuleDesc.of("MyModule"), mab -> mab.uses(foo).provides(foo, foo)))))).findAttribute(Attributes.MODULE).get(); assertEquals(ma.uses().get(0).asSymbol(), bar); @@ -187,10 +190,11 @@ class AdvancedTransformationsTest { var fooAnno = ClassDesc.ofDescriptor(FooAnno.class.descriptorString()); var barAnno = ClassDesc.ofDescriptor(BarAnno.class.descriptorString()); var rec = ClassDesc.ofDescriptor(Rec.class.descriptorString()); - - var remapped = Classfile.parse( + var cc = Classfile.of(); + var remapped = cc.parse( ClassRemapper.of(Map.of(foo, bar, fooAnno, barAnno)).remapClass( - Classfile.parse( + cc, + cc.parse( Rec.class.getResourceAsStream(Rec.class.getName() + ".class") .readAllBytes()))); var sb = new StringBuilder(); @@ -233,10 +237,11 @@ class AdvancedTransformationsTest { @Test void testInstrumentClass() throws Exception { - var instrumentor = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$InstrumentorClass.class").readAllBytes()); - var target = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$TargetClass.class").readAllBytes()); + var cc = Classfile.of(); + var instrumentor = cc.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$InstrumentorClass.class").readAllBytes()); + var target = cc.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$TargetClass.class").readAllBytes()); var instrumentedBytes = instrument(target, instrumentor, mm -> mm.methodName().stringValue().equals("instrumentedMethod")); - assertEmpty(Classfile.parse(instrumentedBytes).verify(null)); //System.out::print)); + assertEmpty(cc.parse(instrumentedBytes).verify(null)); //System.out::print)); var targetClass = new ByteArrayClassLoader(AdvancedTransformationsTest.class.getClassLoader(), "AdvancedTransformationsTest$TargetClass", instrumentedBytes).loadClass("AdvancedTransformationsTest$TargetClass"); assertEquals(targetClass.getDeclaredMethod("instrumentedMethod", Boolean.class).invoke(targetClass.getDeclaredConstructor().newInstance(), false), 34); } @@ -297,7 +302,7 @@ class AdvancedTransformationsTest { var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); - return target.transform( + return Classfile.of().transform(target, ClassTransform.transformingMethods( instrumentedMethodsFilter, (mb, me) -> { diff --git a/test/jdk/jdk/classfile/AnnotationModelTest.java b/test/jdk/jdk/classfile/AnnotationModelTest.java index 529df063db8..f4bb971dbf7 100644 --- a/test/jdk/jdk/classfile/AnnotationModelTest.java +++ b/test/jdk/jdk/classfile/AnnotationModelTest.java @@ -55,7 +55,7 @@ class AnnotationModelTest { @Test void readAnnos() { - var model = Classfile.parse(fileBytes); + var model = Classfile.of().parse(fileBytes); var annotations = model.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get().annotations(); assertEquals(annotations.size(), 3); diff --git a/test/jdk/jdk/classfile/AnnotationTest.java b/test/jdk/jdk/classfile/AnnotationTest.java index e649c0e0a5c..aec457a3f5f 100644 --- a/test/jdk/jdk/classfile/AnnotationTest.java +++ b/test/jdk/jdk/classfile/AnnotationTest.java @@ -127,12 +127,13 @@ class AnnotationTest { @Test void testAnnos() { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("Foo"), cb -> { ((DirectClassBuilder) cb).writeAttribute(buildAnnotationsWithCPB(cb.constantPool())); cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotationsWithCPB(mb.constantPool()))); cb.withField("foo", CD_int, fb -> fb.with(buildAnnotationsWithCPB(fb.constantPool()))); }); - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); List ces = cm.elementList(); List annos = ces.stream() .filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute) @@ -172,12 +173,13 @@ class AnnotationTest { @Test void testAnnosNoCPB() { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("Foo"), cb -> { ((DirectClassBuilder) cb).writeAttribute(buildAnnotations()); cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotations())); cb.withField("foo", CD_int, fb -> fb.with(buildAnnotations())); }); - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); List ces = cm.elementList(); List annos = ces.stream() .filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute) diff --git a/test/jdk/jdk/classfile/ArrayTest.java b/test/jdk/jdk/classfile/ArrayTest.java index 2c2ab0948f2..c9cccc9c1f3 100644 --- a/test/jdk/jdk/classfile/ArrayTest.java +++ b/test/jdk/jdk/classfile/ArrayTest.java @@ -53,7 +53,7 @@ class ArrayTest { @Test void testArrayNew() throws Exception { - ClassModel cm = Classfile.parse(testClassPath); + ClassModel cm = Classfile.of().parse(testClassPath); for (MethodModel mm : cm.methods()) { mm.code().ifPresent(code -> { diff --git a/test/jdk/jdk/classfile/BSMTest.java b/test/jdk/jdk/classfile/BSMTest.java index b2f63b9fb45..e8e6af47b15 100644 --- a/test/jdk/jdk/classfile/BSMTest.java +++ b/test/jdk/jdk/classfile/BSMTest.java @@ -60,8 +60,9 @@ public class BSMTest { @Test void testSevenOfThirteenIterator() throws Exception { - ClassModel cm = Classfile.parse(testClassPath); - byte[] newBytes = cm.transform((cb, ce) -> { + var cc = Classfile.of(); + ClassModel cm = cc.parse(testClassPath); + byte[] newBytes = cc.transform(cm, (cb, ce) -> { if (ce instanceof MethodModel mm) { cb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel xm) { diff --git a/test/jdk/jdk/classfile/BasicBlockTest.java b/test/jdk/jdk/classfile/BasicBlockTest.java index d16219d08ed..fa1dac77239 100644 --- a/test/jdk/jdk/classfile/BasicBlockTest.java +++ b/test/jdk/jdk/classfile/BasicBlockTest.java @@ -57,8 +57,9 @@ class BasicBlockTest { @Test void testPatternsCausingBasicBlockTroubles() throws IOException { try (InputStream in = BasicBlockTest.class.getResourceAsStream("BasicBlockTest.class")) { - var classModel = Classfile.parse(in.readAllBytes()); - Classfile.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb)); + var cc = Classfile.of(); + var classModel = cc.parse(in.readAllBytes()); + cc.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb)); } } } diff --git a/test/jdk/jdk/classfile/BoundAttributeTest.java b/test/jdk/jdk/classfile/BoundAttributeTest.java index 3dc09a79bc0..e5a3af83826 100644 --- a/test/jdk/jdk/classfile/BoundAttributeTest.java +++ b/test/jdk/jdk/classfile/BoundAttributeTest.java @@ -51,16 +51,17 @@ class BoundAttributeTest { @Test void testReadMethodParametersAttributeWithoutParameterName() { + var cc = Classfile.of(); // build a simple method: void method(int) MethodTypeDesc methodTypeDesc = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_int); - byte[] raw = Classfile.build(ClassDesc.of("TestClass"), builder -> { + byte[] raw = cc.build(ClassDesc.of("TestClass"), builder -> { builder.withMethod("method", methodTypeDesc, 0, mb -> { mb.withCode(CodeBuilder::return_); // add a MethodParameters attribute without name for the parameter mb.with(MethodParametersAttribute.of(MethodParameterInfo.ofParameter(Optional.empty(), 0))); }); }); - ClassModel model = Classfile.parse(raw); + ClassModel model = cc.parse(raw); MethodParametersAttribute methodParametersAttribute = model.methods().get(0) .findAttribute(Attributes.METHOD_PARAMETERS) .orElseThrow(() -> new AssertionFailedError("Attribute not present")); diff --git a/test/jdk/jdk/classfile/BuilderBlockTest.java b/test/jdk/jdk/classfile/BuilderBlockTest.java index 45b8904eae0..84c7f394bce 100644 --- a/test/jdk/jdk/classfile/BuilderBlockTest.java +++ b/test/jdk/jdk/classfile/BuilderBlockTest.java @@ -61,7 +61,7 @@ class BuilderBlockTest { // Ensure that start=0 at top level, end is undefined until code is done, then end=1 Label startEnd[] = new Label[2]; - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.withCode(xb -> { startEnd[0] = xb.startLabel(); @@ -80,7 +80,7 @@ class BuilderBlockTest { void testStartEndBlock() throws Exception { Label startEnd[] = new Label[4]; - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.withCode(xb -> { startEnd[0] = xb.startLabel(); @@ -103,7 +103,7 @@ class BuilderBlockTest { @Test void testIfThenReturn() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -122,7 +122,7 @@ class BuilderBlockTest { @Test void testIfThenElseReturn() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -140,7 +140,7 @@ class BuilderBlockTest { @Test void testIfThenBadOpcode() { - Classfile.build(ClassDesc.of("Foo"), cb -> { + Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -160,7 +160,7 @@ class BuilderBlockTest { @Test void testIfThenElseImplicitBreak() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -180,7 +180,7 @@ class BuilderBlockTest { @Test void testIfThenElseExplicitBreak() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -199,7 +199,7 @@ class BuilderBlockTest { @Test void testIfThenElseOpcode() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -224,7 +224,7 @@ class BuilderBlockTest { @Test void testIfThenElseBadOpcode() { - Classfile.build(ClassDesc.of("Foo"), cb -> { + Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), @@ -245,7 +245,7 @@ class BuilderBlockTest { @Test void testAllocateLocal() { - Classfile.build(ClassDesc.of("Foo"), cb -> { + Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, mb -> mb.withCode(xb -> { int slot1 = xb.allocateLocal(TypeKind.IntType); @@ -262,7 +262,7 @@ class BuilderBlockTest { @Test void testAllocateLocalBlock() { - Classfile.build(ClassDesc.of("Foo"), cb -> { + Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, mb -> mb.withCode(xb -> { xb.block(bb -> { @@ -283,7 +283,7 @@ class BuilderBlockTest { @Test void testAllocateLocalIfThen() { - Classfile.build(ClassDesc.of("Foo"), cb -> { + Classfile.of().build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, mb -> mb.withCode(xb -> { xb.iconst_0(); diff --git a/test/jdk/jdk/classfile/BuilderParamTest.java b/test/jdk/jdk/classfile/BuilderParamTest.java index ca49a88005d..c7e6a1f6888 100644 --- a/test/jdk/jdk/classfile/BuilderParamTest.java +++ b/test/jdk/jdk/classfile/BuilderParamTest.java @@ -44,8 +44,8 @@ import static org.junit.jupiter.api.Assertions.*; class BuilderParamTest { @Test void testDirectBuilder() { - - Classfile.build(ClassDesc.of("Foo"), cb -> { + var cc = Classfile.of(); + cc.build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0, mb -> mb.withCode(xb -> { assertEquals(xb.receiverSlot(), 0); @@ -55,8 +55,7 @@ class BuilderParamTest { xb.return_(); })); }); - - Classfile.build(ClassDesc.of("Foo"), cb -> { + cc.build(ClassDesc.of("Foo"), cb -> { cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), ACC_STATIC, mb -> mb.withCode(xb -> { assertEquals(xb.parameterSlot(0), 0); diff --git a/test/jdk/jdk/classfile/BuilderTryCatchTest.java b/test/jdk/jdk/classfile/BuilderTryCatchTest.java index 94365ff7bff..7947dc9c4f9 100644 --- a/test/jdk/jdk/classfile/BuilderTryCatchTest.java +++ b/test/jdk/jdk/classfile/BuilderTryCatchTest.java @@ -171,7 +171,7 @@ class BuilderTryCatchTest { void testTryEmptyCatch() { byte[] bytes = generateTryCatchMethod(catchBuilder -> {}); - boolean anyGotos = Classfile.parse(bytes).methods().stream() + boolean anyGotos = Classfile.of().parse(bytes).methods().stream() .flatMap(mm -> mm.code().stream()) .flatMap(CompoundElement::elementStream) .anyMatch(codeElement -> @@ -182,7 +182,7 @@ class BuilderTryCatchTest { @Test void testEmptyTry() { - byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { mb.withCode(xb -> { @@ -213,7 +213,7 @@ class BuilderTryCatchTest { @Test void testLocalAllocation() throws Throwable { - byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { mb.withCode(xb -> { @@ -276,7 +276,7 @@ class BuilderTryCatchTest { } static byte[] generateTryCatchMethod(Consumer c) { - byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { mb.withCode(xb -> { diff --git a/test/jdk/jdk/classfile/ClassBuildingTest.java b/test/jdk/jdk/classfile/ClassBuildingTest.java index f774ac97608..b8d289dbe6e 100644 --- a/test/jdk/jdk/classfile/ClassBuildingTest.java +++ b/test/jdk/jdk/classfile/ClassBuildingTest.java @@ -49,9 +49,10 @@ import java.util.Objects; public class ClassBuildingTest { @Test public void test() throws Throwable { + var cc = Classfile.of(); ClassModel cm; try (var in = ClassBuildingTest.class.getResourceAsStream("/Outer$1Local.class")) { - cm = Classfile.parse(Objects.requireNonNull(in).readAllBytes()); + cm = cc.parse(Objects.requireNonNull(in).readAllBytes()); } ClassTransform transform = ClassRemapper.of(Map.of(ClassDesc.of("Outer"), ClassDesc.of("Router"))); @@ -60,7 +61,7 @@ public class ClassBuildingTest { transform = transform.andThen(ClassTransform.transformingMethods(MethodTransform.dropping(me -> me instanceof SignatureAttribute))); - MethodHandles.lookup().defineClass(cm.transform(transform)); + MethodHandles.lookup().defineClass(cc.transform(cm, transform)); } } diff --git a/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java b/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java index 1cd69ccc541..5a7e8c4ddd3 100644 --- a/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java +++ b/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java @@ -128,8 +128,8 @@ class ClassHierarchyInfoTest { void transformAndVerifySingle(ClassHierarchyResolver res) throws Exception { Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class"); - var classModel = Classfile.parse(path, Classfile.Option.classHierarchyResolver(res)); - byte[] newBytes = classModel.transform( + var classModel = Classfile.of().parse(path); + byte[] newBytes = Classfile.of(Classfile.ClassHierarchyResolverOption.of(res)).transform(classModel, (clb, cle) -> { if (cle instanceof MethodModel mm) { clb.transformMethod(mm, (mb, me) -> { @@ -143,7 +143,7 @@ class ClassHierarchyInfoTest { else clb.with(cle); }); - var errors = Classfile.parse(newBytes).verify(null); + var errors = Classfile.of().parse(newBytes).verify(null); if (!errors.isEmpty()) { var itr = errors.iterator(); var thrown = itr.next(); diff --git a/test/jdk/jdk/classfile/ClassPrinterTest.java b/test/jdk/jdk/classfile/ClassPrinterTest.java index 9b4491d11be..2ea6fb79147 100644 --- a/test/jdk/jdk/classfile/ClassPrinterTest.java +++ b/test/jdk/jdk/classfile/ClassPrinterTest.java @@ -43,7 +43,8 @@ import static org.junit.jupiter.api.Assertions.*; class ClassPrinterTest { ClassModel getClassModel() { - return Classfile.parse(Classfile.build(ClassDesc.of("Foo"), clb -> + var cc = Classfile.of(); + return cc.parse(cc.build(ClassDesc.of("Foo"), clb -> clb.withVersion(61, 0) .withFlags(Classfile.ACC_PUBLIC) .with(SourceFileAttribute.of("Foo.java")) diff --git a/test/jdk/jdk/classfile/ConstantPoolCopyTest.java b/test/jdk/jdk/classfile/ConstantPoolCopyTest.java index ffb75d5b7a0..fe9f7f52b25 100644 --- a/test/jdk/jdk/classfile/ConstantPoolCopyTest.java +++ b/test/jdk/jdk/classfile/ConstantPoolCopyTest.java @@ -71,13 +71,14 @@ import static org.junit.jupiter.api.Assertions.*; class ConstantPoolCopyTest { private static ClassModel[] rtJarToClassLow(FileSystem fs) { try { + var cc = Classfile.of(); var modules = Stream.of( Files.walk(fs.getPath("modules/java.base/java")), Files.walk(fs.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class"))) .flatMap(p -> p) .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")) .map(ConstantPoolCopyTest::readAllBytes) - .map(bytes -> Classfile.parse(bytes)) + .map(bytes -> cc.parse(bytes)) .toArray(ClassModel[]::new); return modules; } catch (IOException ioe) { diff --git a/test/jdk/jdk/classfile/CorpusTest.java b/test/jdk/jdk/classfile/CorpusTest.java index 03f399cd4f2..9a5fc9ae95d 100644 --- a/test/jdk/jdk/classfile/CorpusTest.java +++ b/test/jdk/jdk/classfile/CorpusTest.java @@ -79,7 +79,8 @@ class CorpusTest { static void splitTableAttributes(String sourceClassFile, String targetClassFile) throws IOException, URISyntaxException { var root = Paths.get(URI.create(CorpusTest.class.getResource("CorpusTest.class").toString())).getParent(); - Files.write(root.resolve(targetClassFile), Classfile.parse(root.resolve(sourceClassFile)).transform(ClassTransform.transformingMethodBodies((cob, coe) -> { + var cc = Classfile.of(); + Files.write(root.resolve(targetClassFile), cc.transform(cc.parse(root.resolve(sourceClassFile)), ClassTransform.transformingMethodBodies((cob, coe) -> { var dcob = (DirectCodeBuilder)cob; var curPc = dcob.curPc(); switch (coe) { @@ -145,8 +146,8 @@ class CorpusTest { try { byte[] transformed = m.shared && m.classTransform != null - ? Classfile.parse(bytes, Classfile.Option.generateStackmap(false)) - .transform(m.classTransform) + ? Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS) + .transform(Classfile.of().parse(bytes), m.classTransform) : m.transform.apply(bytes); Map newDups = findDups(transformed); oldRecord = m.classRecord(bytes); @@ -196,15 +197,15 @@ class CorpusTest { @MethodSource("corpus") void testReadAndTransform(Path path) throws IOException { byte[] bytes = Files.readAllBytes(path); - - var classModel = Classfile.parse(bytes); + var cc = Classfile.of(); + var classModel = cc.parse(bytes); assertEqualsDeep(ClassRecord.ofClassModel(classModel), ClassRecord.ofStreamingElements(classModel), "ClassModel (actual) vs StreamingElements (expected)"); - byte[] newBytes = Classfile.build( + byte[] newBytes = cc.build( classModel.thisClass().asSymbol(), classModel::forEachElement); - var newModel = Classfile.parse(newBytes, Classfile.Option.generateStackmap(false)); + var newModel = cc.parse(newBytes); assertEqualsDeep(ClassRecord.ofClassModel(newModel, CompatibilityFilter.By_ClassBuilder), ClassRecord.ofClassModel(classModel, CompatibilityFilter.By_ClassBuilder), "ClassModel[%s] transformed by ClassBuilder (actual) vs ClassModel before transformation (expected)".formatted(path)); @@ -212,8 +213,10 @@ class CorpusTest { assertEmpty(newModel.verify(null)); //testing maxStack and maxLocals are calculated identically by StackMapGenerator and StackCounter - byte[] noStackMaps = newModel.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); - var noStackModel = Classfile.parse(noStackMaps); + byte[] noStackMaps = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS) + .transform(newModel, + ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); + var noStackModel = cc.parse(noStackMaps); var itStack = newModel.methods().iterator(); var itNoStack = noStackModel.methods().iterator(); while (itStack.hasNext()) { @@ -243,8 +246,9 @@ class CorpusTest { // } private void compareCp(byte[] orig, byte[] transformed) { - var cp1 = Classfile.parse(orig).constantPool(); - var cp2 = Classfile.parse(transformed).constantPool(); + var cc = Classfile.of(); + var cp1 = cc.parse(orig).constantPool(); + var cp2 = cc.parse(transformed).constantPool(); for (int i = 1; i < cp1.entryCount(); i += cp1.entryByIndex(i).width()) { assertEquals(cpiToString(cp1.entryByIndex(i)), cpiToString(cp2.entryByIndex(i))); @@ -267,7 +271,7 @@ class CorpusTest { private static Map findDups(byte[] bytes) { Map dups = new HashMap<>(); - var cf = Classfile.parse(bytes); + var cf = Classfile.of().parse(bytes); var pool = cf.constantPool(); Set entryStrings = new HashSet<>(); for (int i = 1; i < pool.entryCount(); i += pool.entryByIndex(i).width()) { diff --git a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java index 16fce074a41..790bca073d8 100644 --- a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java +++ b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java @@ -47,7 +47,8 @@ class DiscontinuedInstructionsTest { var testClass = "JsrAndRetSample"; var testMethod = "testMethod"; var cd_list = ArrayList.class.describeConstable().get(); - var bytes = Classfile.build(ClassDesc.of(testClass), clb -> clb + var cc = Classfile.of(); + var bytes = cc.build(ClassDesc.of(testClass), clb -> clb .withVersion(JAVA_5_VERSION, 0) .withMethodBody(testMethod, MethodTypeDesc.of(CD_void, cd_list), ACC_PUBLIC | ACC_STATIC, cob -> cob .block(bb -> { @@ -64,7 +65,7 @@ class DiscontinuedInstructionsTest { .pop() .with(DiscontinuedInstruction.RetInstruction.of(355)))); - var c = Classfile.parse(bytes).methods().get(0).code().get(); + var c = cc.parse(bytes).methods().get(0).code().get(); assertEquals(356, c.maxLocals()); assertEquals(6, c.maxStack()); @@ -75,22 +76,27 @@ class DiscontinuedInstructionsTest { .invoke(null, list); assertEquals(list, List.of("Hello", "World")); - bytes = Classfile.parse(bytes).transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); + bytes = cc.transform(cc.parse(bytes), ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); new ByteArrayClassLoader(DiscontinuedInstructionsTest.class.getClassLoader(), testClass, bytes) .getMethod(testClass, testMethod) .invoke(null, list); assertEquals(list, List.of("Hello", "World", "Hello", "World")); - var clm = Classfile.parse(bytes); + var clm = cc.parse(bytes); //test failover stack map generation - clm.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) - .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_6_VERSION, 0)))); + cc.transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) + .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_6_VERSION, 0)))); - //test failure of stack map generation + //test failure of stack map generation for Java 7 assertThrows(IllegalArgumentException.class, () -> - clm.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) - .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_7_VERSION, 0))))); + cc.transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) + .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_7_VERSION, 0))))); + + //test failure of stack map generation when enforced to generate + assertThrows(IllegalArgumentException.class, () -> + Classfile.of(Classfile.StackMapsOption.GENERATE_STACK_MAPS) + .transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL))); } } diff --git a/test/jdk/jdk/classfile/FilterDeadLabelsTest.java b/test/jdk/jdk/classfile/FilterDeadLabelsTest.java index 1608f532ae4..2959ec5219c 100644 --- a/test/jdk/jdk/classfile/FilterDeadLabelsTest.java +++ b/test/jdk/jdk/classfile/FilterDeadLabelsTest.java @@ -60,7 +60,8 @@ class FilterDeadLabelsTest { @Test void testFilterDeadLabels() { - var code = Classfile.parse(Classfile.build(ClassDesc.of("cls"), List.of(Classfile.Option.filterDeadLabels(true)), clb -> + var cc = Classfile.of(Classfile.DeadLabelsOption.DROP_DEAD_LABELS); + var code = cc.parse(cc.build(ClassDesc.of("cls"), clb -> clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { cob.return_(); deadLabelFragments().forEach(f -> f.accept(cob)); @@ -75,7 +76,7 @@ class FilterDeadLabelsTest { @ParameterizedTest @MethodSource("deadLabelFragments") void testThrowOnDeadLabels(Consumer fragment) { - assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("cls"), clb -> + assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("cls"), clb -> clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { cob.return_(); fragment.accept(cob); diff --git a/test/jdk/jdk/classfile/LDCTest.java b/test/jdk/jdk/classfile/LDCTest.java index 482f677fdfe..66c3b0fcfa9 100644 --- a/test/jdk/jdk/classfile/LDCTest.java +++ b/test/jdk/jdk/classfile/LDCTest.java @@ -46,7 +46,8 @@ import jdk.internal.classfile.instruction.ConstantInstruction; class LDCTest { @Test void testLDCisConvertedToLDCW() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb @@ -75,7 +76,7 @@ class LDCTest { })); }); - var model = Classfile.parse(bytes); + var model = cc.parse(bytes); var code = model.elementStream() .filter(e -> e instanceof MethodModel) .map(e -> (MethodModel) e) diff --git a/test/jdk/jdk/classfile/LimitsTest.java b/test/jdk/jdk/classfile/LimitsTest.java index 83b4c37cc56..aa7f88240f5 100644 --- a/test/jdk/jdk/classfile/LimitsTest.java +++ b/test/jdk/jdk/classfile/LimitsTest.java @@ -39,7 +39,7 @@ class LimitsTest { @Test void testCPSizeLimit() { - Classfile.build(ClassDesc.of("BigClass"), cb -> { + Classfile.of().build(ClassDesc.of("BigClass"), cb -> { for (int i = 1; i < 65000; i++) { cb.withField("field" + i, ConstantDescs.CD_int, fb -> {}); } @@ -48,7 +48,7 @@ class LimitsTest { @Test void testCPOverLimit() { - assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("BigClass"), cb -> { + assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("BigClass"), cb -> { for (int i = 1; i < 66000; i++) { cb.withField("field" + i, ConstantDescs.CD_int, fb -> {}); } @@ -57,7 +57,7 @@ class LimitsTest { @Test void testCodeOverLimit() { - assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("BigClass"), cb -> cb.withMethodBody( + assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("BigClass"), cb -> cb.withMethodBody( "bigMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { for (int i = 0; i < 65535; i++) { cob.nop(); @@ -68,7 +68,7 @@ class LimitsTest { @Test void testEmptyCode() { - assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("EmptyClass"), cb -> cb.withMethodBody( + assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("EmptyClass"), cb -> cb.withMethodBody( "emptyMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {}))); } } diff --git a/test/jdk/jdk/classfile/LowAdaptTest.java b/test/jdk/jdk/classfile/LowAdaptTest.java index a50514789f0..421ed673b0c 100644 --- a/test/jdk/jdk/classfile/LowAdaptTest.java +++ b/test/jdk/jdk/classfile/LowAdaptTest.java @@ -56,7 +56,8 @@ class LowAdaptTest { @Test void testAdapt() throws Exception { - ClassModel cl = Classfile.parse(Paths.get(URI.create(LowAdaptTest.class.getResource(test + ".class").toString()))); + var cc = Classfile.of(); + ClassModel cl = cc.parse(Paths.get(URI.create(LowAdaptTest.class.getResource(test + ".class").toString()))); DirectMethodHandleDesc bsm = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of("java.lang.invoke.LambdaMetafactory"), @@ -73,7 +74,7 @@ class LowAdaptTest { MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of(test), "fib", "(I)I"), MethodTypeDesc.ofDescriptor("(I)I")); - byte[] clazz = Classfile.build(ClassDesc.of(test), cb -> { + byte[] clazz = cc.build(ClassDesc.of(test), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.with(SourceFileAttribute.of("/some/madeup/TestClass.java")); cl.methods().forEach(m -> ((DirectClassBuilder) cb).withMethod(m)); diff --git a/test/jdk/jdk/classfile/LowJCovAttributeTest.java b/test/jdk/jdk/classfile/LowJCovAttributeTest.java index 7767d945920..7ea8ae26d63 100644 --- a/test/jdk/jdk/classfile/LowJCovAttributeTest.java +++ b/test/jdk/jdk/classfile/LowJCovAttributeTest.java @@ -61,7 +61,7 @@ class LowJCovAttributeTest { LowJCovAttributeTest() throws IOException { this.path = Paths.get(URI.create(LowJCovAttributeTest.class.getResource(TEST_FILE).toString())); - this.classLow = Classfile.parse(path); + this.classLow = Classfile.of().parse(path); } @Test diff --git a/test/jdk/jdk/classfile/LowModuleTest.java b/test/jdk/jdk/classfile/LowModuleTest.java index 5886ada824c..7927ce5794b 100644 --- a/test/jdk/jdk/classfile/LowModuleTest.java +++ b/test/jdk/jdk/classfile/LowModuleTest.java @@ -68,7 +68,7 @@ class LowModuleTest { void testRead(Path path, TestInfo test) throws Exception { try { printf("%nCHECK %s%n", test.getDisplayName()); - ClassModel classLow = Classfile.parse(path); + ClassModel classLow = Classfile.of().parse(path); testRead0(classLow); } catch(Exception ex) { System.err.printf("%nFAIL %s - %s%n", path, ex); diff --git a/test/jdk/jdk/classfile/LvtTest.java b/test/jdk/jdk/classfile/LvtTest.java index 41231c6434e..f4bd9ed2fc0 100644 --- a/test/jdk/jdk/classfile/LvtTest.java +++ b/test/jdk/jdk/classfile/LvtTest.java @@ -81,7 +81,7 @@ class LvtTest { @Test void getLVTEntries() { - ClassModel c = Classfile.parse(fileBytes); + ClassModel c = Classfile.of().parse(fileBytes); CodeModel co = c.methods().stream() .filter(mm -> mm.methodName().stringValue().equals("m")) .map(MethodModel::code) @@ -106,18 +106,20 @@ class LvtTest { @Test void buildLVTEntries() throws Exception { - ClassModel c = Classfile.parse(fileBytes); + var cc = Classfile.of(); + ClassModel c = cc.parse(fileBytes); // Compare transformed model and original with CodeBuilder filter - byte[] newClass = c.transform(Transforms.threeLevelNoop); - ClassRecord orig = ClassRecord.ofClassModel(Classfile.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder); - ClassRecord transformed = ClassRecord.ofClassModel(Classfile.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder); + byte[] newClass = cc.transform(c, Transforms.threeLevelNoop); + ClassRecord orig = ClassRecord.ofClassModel(cc.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder); + ClassRecord transformed = ClassRecord.ofClassModel(cc.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder); ClassRecord.assertEqualsDeep(transformed, orig); } @Test void testCreateLoadLVT() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) @@ -172,7 +174,7 @@ class LvtTest { })); }); - var c = Classfile.parse(bytes); + var c = cc.parse(bytes); var main = c.methods().get(1); var lvt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TABLE).get(); var lvs = lvt.localVariables(); @@ -189,7 +191,7 @@ class LvtTest { @Test void getLVTTEntries() { - ClassModel c = Classfile.parse(fileBytes); + ClassModel c = Classfile.of().parse(fileBytes); CodeModel co = c.methods().stream() .filter(mm -> mm.methodName().stringValue().equals("n")) .map(MethodModel::code) @@ -229,7 +231,8 @@ class LvtTest { @Test void testCreateLoadLVTT() throws Exception { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) @@ -275,7 +278,7 @@ class LvtTest { .localVariable(1, u, jlObject, start, end); })); }); - var c = Classfile.parse(bytes); + var c = cc.parse(bytes); var main = c.methods().get(1); var lvtt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE).get(); var lvts = lvtt.localVariableTypes(); @@ -301,7 +304,7 @@ class LvtTest { @Test void skipDebugSkipsLVT() { - ClassModel c = Classfile.parse(fileBytes, Classfile.Option.processDebug(false)); + ClassModel c = Classfile.of(Classfile.DebugElementsOption.DROP_DEBUG).parse(fileBytes); c.forEachElement(e -> { if (e instanceof MethodModel m) { diff --git a/test/jdk/jdk/classfile/MassAdaptCopyCodeTest.java b/test/jdk/jdk/classfile/MassAdaptCopyCodeTest.java index 9cbba5e9d89..5e7a1d789b3 100644 --- a/test/jdk/jdk/classfile/MassAdaptCopyCodeTest.java +++ b/test/jdk/jdk/classfile/MassAdaptCopyCodeTest.java @@ -76,13 +76,13 @@ class MassAdaptCopyCodeTest { } void copy(String name, byte[] bytes) throws Exception { - byte[] newBytes = adaptCopy(Classfile.parse(bytes)); + byte[] newBytes = adaptCopy(Classfile.of().parse(bytes)); classNameToClass.put(name, new ByteArrayClassLoader.ClassData(name, newBytes)); if (name.contains("/")) throw new RuntimeException(name); } public byte[] adaptCopy(ClassModel cm) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { if (ce instanceof MethodModel mm) { cb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel xm) { diff --git a/test/jdk/jdk/classfile/MassAdaptCopyPrimitiveMatchCodeTest.java b/test/jdk/jdk/classfile/MassAdaptCopyPrimitiveMatchCodeTest.java index d43a79cbf64..4af96989d0d 100644 --- a/test/jdk/jdk/classfile/MassAdaptCopyPrimitiveMatchCodeTest.java +++ b/test/jdk/jdk/classfile/MassAdaptCopyPrimitiveMatchCodeTest.java @@ -96,11 +96,12 @@ class MassAdaptCopyPrimitiveMatchCodeTest { void copy(String name, byte[] bytes) throws Exception { //System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest - %s%n", name); - ClassModel cm =(Classfile.parse(bytes)); + var cc = Classfile.of(); + ClassModel cm =cc.parse(bytes); Map m2b = new HashMap<>(); Map m2c = new HashMap<>(); byte[] resultBytes = - cm.transform((cb, e) -> { + cc.transform(cm, (cb, e) -> { if (e instanceof MethodModel mm) { Optional code = mm.code(); if (code.isPresent()) { @@ -125,7 +126,7 @@ class MassAdaptCopyPrimitiveMatchCodeTest { System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest: Ignored because it is a record%n - %s%n", name); return; } - ClassModel rcm = Classfile.parse(resultBytes); + ClassModel rcm = cc.parse(resultBytes); for (MethodModel rmm : rcm.methods()) { Optional code = rmm.code(); if (code.isPresent()) { diff --git a/test/jdk/jdk/classfile/ModuleBuilderTest.java b/test/jdk/jdk/classfile/ModuleBuilderTest.java index 03845db3221..6bb32aecffe 100644 --- a/test/jdk/jdk/classfile/ModuleBuilderTest.java +++ b/test/jdk/jdk/classfile/ModuleBuilderTest.java @@ -64,7 +64,8 @@ class ModuleBuilderTest { private final ModuleAttribute attr; public ModuleBuilderTest() { - byte[] modInfo = Classfile.buildModule( + var cc = Classfile.of(); + byte[] modInfo = cc.buildModule( ModuleAttribute.of(modName, mb -> mb .moduleVersion(modVsn) @@ -88,7 +89,7 @@ class ModuleBuilderTest { clb -> clb.with(ModuleMainClassAttribute.of(ClassDesc.of("main.Class"))) .with(ModulePackagesAttribute.ofNames(PackageDesc.of("foo.bar.baz"), PackageDesc.of("quux"))) .with(ModuleMainClassAttribute.of(ClassDesc.of("overwritten.main.Class")))); - moduleModel = Classfile.parse(modInfo); + moduleModel = cc.parse(modInfo); attr = ((ModuleAttribute) moduleModel.attributes().stream() .filter(a -> a.attributeMapper() == Attributes.MODULE) .findFirst() @@ -98,10 +99,11 @@ class ModuleBuilderTest { @Test void testCreateModuleInfo() { // Build the module-info.class bytes - byte[] modBytes = Classfile.buildModule(ModuleAttribute.of(modName, mb -> mb.moduleVersion(modVsn))); + var cc = Classfile.of(); + byte[] modBytes = cc.buildModule(ModuleAttribute.of(modName, mb -> mb.moduleVersion(modVsn))); // Verify - var cm = Classfile.parse(modBytes); + var cm = cc.parse(modBytes); var attr =cm.findAttribute(Attributes.MODULE).get(); assertEquals(attr.moduleName().name().stringValue(), modName.name()); @@ -195,7 +197,7 @@ class ModuleBuilderTest { void verifyIsModuleInfo() throws Exception { assertTrue(moduleModel.isModuleInfo()); - ClassModel m = Classfile.parse(Paths.get(URI.create(ModuleBuilderTest.class.getResource("ModuleBuilderTest.class").toString()))); + ClassModel m = Classfile.of().parse(Paths.get(URI.create(ModuleBuilderTest.class.getResource("ModuleBuilderTest.class").toString()))); assertFalse(m.isModuleInfo()); } } diff --git a/test/jdk/jdk/classfile/OneToOneTest.java b/test/jdk/jdk/classfile/OneToOneTest.java index 6e85b4046cb..31f97f7e969 100644 --- a/test/jdk/jdk/classfile/OneToOneTest.java +++ b/test/jdk/jdk/classfile/OneToOneTest.java @@ -63,8 +63,8 @@ class OneToOneTest { @Test void testClassWriteRead() { - - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) @@ -107,7 +107,7 @@ class OneToOneTest { } ); - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); List ms = cm.methods(); assertEquals(ms.size(), 2); boolean found = false; diff --git a/test/jdk/jdk/classfile/OpcodesValidationTest.java b/test/jdk/jdk/classfile/OpcodesValidationTest.java index 43e2ec630d5..6cf63151470 100644 --- a/test/jdk/jdk/classfile/OpcodesValidationTest.java +++ b/test/jdk/jdk/classfile/OpcodesValidationTest.java @@ -105,7 +105,7 @@ public class OpcodesValidationTest { } private void testPositiveCase(Opcode opcode, Object constant) { - Classfile.build(ClassDesc.of("MyClass"), + Classfile.of().build(ClassDesc.of("MyClass"), cb -> cb.withFlags(AccessFlag.PUBLIC) .withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb.withCode( @@ -122,7 +122,7 @@ public class OpcodesValidationTest { } private void testNegativeCase(Opcode opcode, Object constant) { - Classfile.build(ClassDesc.of("MyClass"), + Classfile.of().build(ClassDesc.of("MyClass"), cb -> cb.withFlags(AccessFlag.PUBLIC) .withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb .withCode( diff --git a/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java b/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java index f4e14c3da43..c8b9d757d26 100644 --- a/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java +++ b/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java @@ -53,7 +53,7 @@ public final class PrimitiveClassConstantTest { public void test() throws Throwable { ClassDesc ape = ClassDesc.of("Ape"); var lookup = MethodHandles.lookup(); - Class a = lookup.defineClass(Classfile.build(ape, clb -> { + Class a = lookup.defineClass(Classfile.of().build(ape, clb -> { clb.withSuperclass(CD_Object); clb.withInterfaceSymbols(Supplier.class.describeConstable().orElseThrow()); clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> { diff --git a/test/jdk/jdk/classfile/ShortJumpsFixTest.java b/test/jdk/jdk/classfile/ShortJumpsFixTest.java index dedef7e1321..7d5448f1c27 100644 --- a/test/jdk/jdk/classfile/ShortJumpsFixTest.java +++ b/test/jdk/jdk/classfile/ShortJumpsFixTest.java @@ -33,6 +33,7 @@ import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; import java.util.LinkedList; import java.util.List; +import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.Classfile; import jdk.internal.classfile.Instruction; @@ -99,109 +100,114 @@ class ShortJumpsFixTest { }; } + static final Classfile + CC_Fixed_Jumps = Classfile.of(Classfile.ShortJumpsOption.FIX_SHORT_JUMPS), + CC_Not_Fixed_Jumps = Classfile.of(Classfile.ShortJumpsOption.FAIL_ON_SHORT_JUMPS), + CC_No_Stack_No_Patch = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS, + Classfile.DeadCodeOption.KEEP_DEAD_CODE); @ParameterizedTest @MethodSource("provideFwd") void testFixFwdJumpsDirectGen(Sample sample) throws Exception { - assertFixed(sample, generateFwd(sample, true, Classfile.Option.fixShortJumps(true))); + assertFixed(sample, + generateFwd(CC_Fixed_Jumps, sample, true)); } @ParameterizedTest @MethodSource("provideBack") void testFixBackJumpsDirectGen(Sample sample) throws Exception { - assertFixed(sample, generateBack(sample, true, Classfile.Option.fixShortJumps(true))); + assertFixed(sample, + generateBack(CC_Fixed_Jumps, sample, true)); } @ParameterizedTest @MethodSource("provideFwd") void testFailFwdJumpsDirectGen(Sample sample) throws Exception { - assertThrows(IllegalArgumentException.class, () -> generateFwd(sample, true, Classfile.Option.fixShortJumps(false))); + assertThrows(IllegalArgumentException.class, () -> + generateFwd(CC_Not_Fixed_Jumps, sample, true)); } @ParameterizedTest @MethodSource("provideBack") void testFailBackJumpsDirectGen(Sample sample) throws Exception { - assertThrows(IllegalArgumentException.class, () -> generateBack(sample, true, Classfile.Option.fixShortJumps(false))); + assertThrows(IllegalArgumentException.class, () -> + generateBack(CC_Not_Fixed_Jumps, sample, true)); } @ParameterizedTest @MethodSource("provideFwd") void testFixFwdJumpsTransform(Sample sample) throws Exception { - assertFixed(sample, Classfile.parse( - generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(true)) - .transform(overflow())); + assertFixed(sample, + CC_Fixed_Jumps.transform( + generateFwd(CC_No_Stack_No_Patch, sample, false), + overflow())); } @ParameterizedTest @MethodSource("provideBack") void testFixBackJumpsTransform(Sample sample) throws Exception { - assertFixed(sample, Classfile.parse( - generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(true)) - .transform(overflow())); + assertFixed(sample, + CC_Fixed_Jumps.transform( + generateBack(CC_No_Stack_No_Patch, sample, false), + overflow())); } @ParameterizedTest @MethodSource("provideFwd") void testFailFwdJumpsTransform(Sample sample) throws Exception { assertThrows(IllegalArgumentException.class, () -> - Classfile.parse( - generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(false)) - .transform(overflow())); + CC_Not_Fixed_Jumps.transform( + generateFwd(CC_No_Stack_No_Patch, sample, false), + overflow())); } @ParameterizedTest @MethodSource("provideBack") void testFailBackJumpsTransform(Sample sample) throws Exception { assertThrows(IllegalArgumentException.class, () -> - Classfile.parse( - generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(false)) - .transform(overflow())); + CC_Not_Fixed_Jumps.transform( + generateBack(CC_No_Stack_No_Patch, sample, false), + overflow())); } @ParameterizedTest @MethodSource("provideFwd") void testFixFwdJumpsChainedTransform(Sample sample) throws Exception { - assertFixed(sample, Classfile.parse( - generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(true)) - .transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here + assertFixed(sample, + CC_Fixed_Jumps.transform( + generateFwd(CC_No_Stack_No_Patch, sample, false), + ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here } @ParameterizedTest @MethodSource("provideBack") void testFixBackJumpsChainedTransform(Sample sample) throws Exception { - assertFixed(sample, Classfile.parse( - generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(true)) - .transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here + assertFixed(sample, + CC_Fixed_Jumps.transform( + generateBack(CC_No_Stack_No_Patch, sample, false), + ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here } @ParameterizedTest @MethodSource("provideFwd") void testFailFwdJumpsChainedTransform(Sample sample) throws Exception { assertThrows(IllegalArgumentException.class, () -> - Classfile.parse( - generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(false)) - .transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here + CC_Not_Fixed_Jumps.transform( + generateFwd(CC_No_Stack_No_Patch, sample, false), + ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here } @ParameterizedTest @MethodSource("provideBack") void testFailBackJumpsChainedTransform(Sample sample) throws Exception { assertThrows(IllegalArgumentException.class, () -> - Classfile.parse( - generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), - Classfile.Option.fixShortJumps(false)) - .transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here + CC_Not_Fixed_Jumps.transform( + generateBack(CC_No_Stack_No_Patch, sample, false), + ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here } - private static byte[] generateFwd(Sample sample, boolean overflow, Classfile.Option... options) { - return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options), + private static ClassModel generateFwd(Classfile cc, Sample sample, boolean overflow) { + return cc.parse(cc.build(ClassDesc.of("WhateverClass"), cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, mb -> mb.withCode(cob -> { for (int i = 0; i < sample.expected.length - 4; i++) //cherry-pick XCONST_ instructions from expected output @@ -212,11 +218,11 @@ class ShortJumpsFixTest { cob.nopInstruction(); cob.labelBinding(target); cob.return_(); - }))); + })))); } - private static byte[] generateBack(Sample sample, boolean overflow, Classfile.Option... options) { - return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options), + private static ClassModel generateBack(Classfile cc, Sample sample, boolean overflow) { + return cc.parse(cc.build(ClassDesc.of("WhateverClass"), cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, mb -> mb.withCode(cob -> { var target = cob.newLabel(); @@ -231,7 +237,7 @@ class ShortJumpsFixTest { cob.with(ConstantInstruction.ofIntrinsic(sample.expected[i])); cob.branchInstruction(sample.jumpCode, target); cob.return_(); - }))); + })))); } private static ClassTransform overflow() { @@ -246,8 +252,12 @@ class ShortJumpsFixTest { } private static void assertFixed(Sample sample, byte[] classFile) { + assertFixed(sample, Classfile.of().parse(classFile)); + } + + private static void assertFixed(Sample sample, ClassModel clm) { var found = new LinkedList(); - for (var e : Classfile.parse(classFile).methods().get(0).code().get()) + for (var e : clm.methods().get(0).code().get()) if (e instanceof Instruction i && found.peekLast() != i.opcode()) //dedup subsequent (NOPs) found.add(i.opcode()); assertEquals(found, List.of(sample.expected)); diff --git a/test/jdk/jdk/classfile/SignaturesTest.java b/test/jdk/jdk/classfile/SignaturesTest.java index 2827ce177c7..b99887fb539 100644 --- a/test/jdk/jdk/classfile/SignaturesTest.java +++ b/test/jdk/jdk/classfile/SignaturesTest.java @@ -128,7 +128,7 @@ class SignaturesTest { .flatMap(p -> p) .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> { try { - var cm = Classfile.parse(path); + var cm = Classfile.of().parse(path); cm.findAttribute(Attributes.SIGNATURE).ifPresent(csig -> { assertEquals( ClassSignature.parseFrom(csig.signature().stringValue()).signatureString(), diff --git a/test/jdk/jdk/classfile/SnippetsTest.java b/test/jdk/jdk/classfile/SnippetsTest.java new file mode 100644 index 00000000000..abb4c773fde --- /dev/null +++ b/test/jdk/jdk/classfile/SnippetsTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Compile Classfile API snippets + * @run junit SnippetsTest + */ + +import java.nio.file.Paths; +import java.util.List; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class SnippetsTest { + + @ParameterizedTest + @ValueSource(strings = { + "src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java", + "src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java"}) + void testSnippet(String source) throws Exception { + var p = Paths.get(System.getProperty("test.src", ".")).toAbsolutePath(); + while ((p = p.getParent()) != null) { + var src = p.resolve(source).toFile(); + if (src.isFile()) { + var compiler = ToolProvider.getSystemJavaCompiler(); + try (var fileManager = compiler.getStandardFileManager(null, null, null)) { + var compilationUnits = fileManager.getJavaFileObjectsFromFiles(List.of(src)); + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, + List.of(Paths.get(System.getProperty("test.classes", ".")).toFile())); + var task = compiler.getTask(null, fileManager, null, List.of( + "--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.classfile.attribute=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED"), + null, compilationUnits); + if (task.call()) return; + throw new RuntimeException("Error compiling " + source); + } + } + } + Assumptions.abort("Source file not found: " + source); //do not fail in source-less test environment + } +} diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 0d5a4053085..0d9ab68cf3b 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -54,9 +54,9 @@ import java.lang.reflect.AccessFlag; class StackMapsTest { private byte[] buildDeadCode() { - return Classfile.build( + return Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS, + Classfile.DeadCodeOption.KEEP_DEAD_CODE).build( ClassDesc.of("DeadCodePattern"), - List.of(Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), clb -> clb.withMethodBody( "twoReturns", MethodTypeDesc.of(ConstantDescs.CD_void), @@ -97,7 +97,7 @@ class StackMapsTest { @Test void testDeadCodePatternFail() throws Exception { - var error = assertThrows(IllegalArgumentException.class, () -> testTransformedStackMaps(buildDeadCode(), Classfile.Option.patchDeadCode(false))); + var error = assertThrows(IllegalArgumentException.class, () -> testTransformedStackMaps(buildDeadCode(), Classfile.DeadCodeOption.KEEP_DEAD_CODE)); assertLinesMatch( """ Unable to generate stack map frame for dead code at bytecode offset 1 of method twoReturns() @@ -172,9 +172,10 @@ class StackMapsTest { @Test void testFrameOutOfBytecodeRange() { + var cc = Classfile.of(); var error = assertThrows(IllegalArgumentException.class, () -> - Classfile.parse( - Classfile.build(ClassDesc.of("TestClass"), clb -> + cc.parse( + cc.build(ClassDesc.of("TestClass"), clb -> clb.withMethodBody("frameOutOfRangeMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { var l = cob.newLabel(); cob.goto_(l);//jump to the end of method body triggers invalid frame creation @@ -194,7 +195,7 @@ class StackMapsTest { @Test void testMethodSwitchFromStatic() { assertThrows(IllegalArgumentException.class, () -> - Classfile.build(ClassDesc.of("TestClass"), clb -> + Classfile.of().build(ClassDesc.of("TestClass"), clb -> clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_Object, ConstantDescs.CD_int), ACC_STATIC, mb -> mb.withCode(cob -> { @@ -207,7 +208,7 @@ class StackMapsTest { @Test void testMethodSwitchToStatic() { assertThrows(IllegalArgumentException.class, () -> - Classfile.build(ClassDesc.of("TestClass"), clb -> + Classfile.of().build(ClassDesc.of("TestClass"), clb -> clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int), 0, mb -> mb.withCode(cob -> { @@ -219,16 +220,23 @@ class StackMapsTest { @Test void testClassVersions() throws Exception { - var actualVersion = Classfile.parse(StackMapsTest.class.getResourceAsStream("/testdata/Pattern1.class").readAllBytes()); + var cc = Classfile.of(); + var actualVersion = cc.parse(StackMapsTest.class.getResourceAsStream("/testdata/Pattern1.class").readAllBytes()); //test transformation to class version 49 with removal of StackMapTable attributes - var version49 = Classfile.parse(actualVersion.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) - .andThen(ClassTransform.endHandler(clb -> clb.withVersion(49, 0))))); - assertFalse(ClassPrinter.toTree(version49, ClassPrinter.Verbosity.CRITICAL_ATTRIBUTES).walk().anyMatch(n -> n.name().equals("stack map frames"))); + var version49 = cc.parse(cc.transform( + actualVersion, + ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) + .andThen(ClassTransform.endHandler(clb -> clb.withVersion(49, 0))))); + assertFalse(ClassPrinter.toTree(version49, ClassPrinter.Verbosity.CRITICAL_ATTRIBUTES) + .walk().anyMatch(n -> n.name().equals("stack map frames"))); //test transformation to class version 50 with re-generation of StackMapTable attributes - assertEmpty(Classfile.parse(version49.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) - .andThen(ClassTransform.endHandler(clb -> clb.withVersion(50, 0))))).verify(null)); + assertEmpty(cc.parse(cc.transform( + version49, + ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) + .andThen(ClassTransform.endHandler(clb -> clb.withVersion(50, 0))))) + .verify(null)); } private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/")); @@ -243,8 +251,9 @@ class StackMapsTest { private static void testTransformedStackMaps(byte[] originalBytes, Classfile.Option... options) throws Exception { //transform the class model - var classModel = Classfile.parse(originalBytes, options); - var transformedBytes = Classfile.build(classModel.thisClass().asSymbol(), List.of(options), + Classfile cc = Classfile.of(options); + var classModel = cc.parse(originalBytes); + var transformedBytes = cc.build(classModel.thisClass().asSymbol(), cb -> { // classModel.superclass().ifPresent(cb::withSuperclass); // cb.withInterfaces(classModel.interfaces()); @@ -253,6 +262,6 @@ class StackMapsTest { }); //then verify transformed bytecode - assertEmpty(Classfile.parse(transformedBytes).verify(null)); + assertEmpty(cc.parse(transformedBytes).verify(null)); } } diff --git a/test/jdk/jdk/classfile/StackTrackerTest.java b/test/jdk/jdk/classfile/StackTrackerTest.java index bfa2b033ff5..3ea80869cac 100644 --- a/test/jdk/jdk/classfile/StackTrackerTest.java +++ b/test/jdk/jdk/classfile/StackTrackerTest.java @@ -45,7 +45,7 @@ class StackTrackerTest { @Test void testStackTracker() { - Classfile.build(ClassDesc.of("Foo"), clb -> + Classfile.of().build(ClassDesc.of("Foo"), clb -> clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> { var stackTracker = CodeStackTracker.of(DoubleType, FloatType); //initial stack tracker pre-set cob.transforming(stackTracker, stcb -> { @@ -81,7 +81,7 @@ class StackTrackerTest { @Test void testTrackingLost() { - Classfile.build(ClassDesc.of("Foo"), clb -> + Classfile.of().build(ClassDesc.of("Foo"), clb -> clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> { var stackTracker = CodeStackTracker.of(); cob.transforming(stackTracker, stcb -> { diff --git a/test/jdk/jdk/classfile/StreamedVsListTest.java b/test/jdk/jdk/classfile/StreamedVsListTest.java index 8b014810379..9cc5d1219f8 100644 --- a/test/jdk/jdk/classfile/StreamedVsListTest.java +++ b/test/jdk/jdk/classfile/StreamedVsListTest.java @@ -78,7 +78,7 @@ class StreamedVsListTest { private class Vs { boolean failed; - ClassModel cm = Classfile.parse(fileBytes); + ClassModel cm = Classfile.of().parse(fileBytes); String meth; CodeElement iim; CodeElement mim; diff --git a/test/jdk/jdk/classfile/SwapTest.java b/test/jdk/jdk/classfile/SwapTest.java index ebf2335d023..15c58149a36 100644 --- a/test/jdk/jdk/classfile/SwapTest.java +++ b/test/jdk/jdk/classfile/SwapTest.java @@ -47,7 +47,7 @@ class SwapTest { MethodType mt = MethodType.methodType(String.class, String.class, String.class); MethodTypeDesc mtd = mt.describeConstable().get(); - byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> { cb.withMethodBody("m", mtd, AccessFlags.ofMethod(PUBLIC, STATIC).flagsMask(), xb -> { xb.aload(0); // 0 xb.aload(1); // 1, 0 diff --git a/test/jdk/jdk/classfile/TempConstantPoolBuilderTest.java b/test/jdk/jdk/classfile/TempConstantPoolBuilderTest.java index a1881932cbf..c423b828f67 100644 --- a/test/jdk/jdk/classfile/TempConstantPoolBuilderTest.java +++ b/test/jdk/jdk/classfile/TempConstantPoolBuilderTest.java @@ -55,7 +55,8 @@ class TempConstantPoolBuilderTest { @Test void addAnno() { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + var cc = Classfile.of(); + byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC) .with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb @@ -67,7 +68,7 @@ class TempConstantPoolBuilderTest { AnnotationElement.ofString("foo", "bar")))) ); }); - ClassModel m = Classfile.parse(bytes); + ClassModel m = cc.parse(bytes); //ClassPrinter.toJson(m, ClassPrinter.Verbosity.TRACE_ALL, System.out::println); } } diff --git a/test/jdk/jdk/classfile/TestRecordComponent.java b/test/jdk/jdk/classfile/TestRecordComponent.java index 4aeb4f65b4c..97b3a209ec2 100644 --- a/test/jdk/jdk/classfile/TestRecordComponent.java +++ b/test/jdk/jdk/classfile/TestRecordComponent.java @@ -55,7 +55,8 @@ class TestRecordComponent { @Test void testAdapt() throws Exception { - ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); + var cc = Classfile.of(); + ClassModel cm = cc.parse(Files.readAllBytes(testClassPath)); ClassTransform xform = (cb, ce) -> { if (ce instanceof RecordAttribute rm) { List components = rm.components(); @@ -66,21 +67,23 @@ class TestRecordComponent { } else cb.with(ce); }; - ClassModel newModel = Classfile.parse(cm.transform(xform)); + ClassModel newModel = cc.parse(cc.transform(cm, xform)); ClassRecord.assertEquals(newModel, cm); } @Test void testPassThrough() throws Exception { - ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); + var cc = Classfile.of(); + ClassModel cm = cc.parse(Files.readAllBytes(testClassPath)); ClassTransform xform = (cb, ce) -> cb.with(ce); - ClassModel newModel = Classfile.parse(cm.transform(xform)); + ClassModel newModel = cc.parse(cc.transform(cm, xform)); ClassRecord.assertEquals(newModel, cm); } @Test void testChagne() throws Exception { - ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); + var cc = Classfile.of(); + ClassModel cm = cc.parse(Files.readAllBytes(testClassPath)); ClassTransform xform = (cb, ce) -> { if (ce instanceof RecordAttribute ra) { List components = ra.components(); @@ -91,7 +94,7 @@ class TestRecordComponent { else cb.with(ce); }; - ClassModel newModel = Classfile.parse(cm.transform(xform)); + ClassModel newModel = cc.parse(cc.transform(cm, xform)); RecordAttribute ra = newModel.findAttribute(Attributes.RECORD).orElseThrow(); assertEquals(ra.components().size(), 2, "Should have two components"); assertEquals(ra.components().get(0).name().stringValue(), "fooXYZ"); @@ -103,7 +106,7 @@ class TestRecordComponent { @Test void testOptions() throws Exception { AtomicInteger count = new AtomicInteger(0); - ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); + ClassModel cm = Classfile.of().parse(Files.readAllBytes(testClassPath)); cm.forEachElement((ce) -> { if (ce instanceof RecordAttribute rm) { count.addAndGet(rm.components().size()); diff --git a/test/jdk/jdk/classfile/TransformTests.java b/test/jdk/jdk/classfile/TransformTests.java index f4dce142598..92011930b67 100644 --- a/test/jdk/jdk/classfile/TransformTests.java +++ b/test/jdk/jdk/classfile/TransformTests.java @@ -95,34 +95,37 @@ class TransformTests { void testSingleTransform() throws Exception { byte[] bytes = Files.readAllBytes(testClassPath); - ClassModel cm = Classfile.parse(bytes); + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); assertEquals(invoke(bytes), "foo"); - assertEquals(invoke(cm.transform(transformCode(foo2foo))), "foo"); - assertEquals(invoke(cm.transform(transformCode(foo2bar))), "bar"); + assertEquals(invoke(cc.transform(cm, transformCode(foo2foo))), "foo"); + assertEquals(invoke(cc.transform(cm, transformCode(foo2bar))), "bar"); } @Test void testSeq2() throws Exception { byte[] bytes = Files.readAllBytes(testClassPath); - ClassModel cm = Classfile.parse(bytes); + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); assertEquals(invoke(bytes), "foo"); ClassTransform transform = transformCode(foo2bar.andThen(bar2baz)); - assertEquals(invoke(cm.transform(transform)), "baz"); + assertEquals(invoke(cc.transform(cm, transform)), "baz"); } @Test void testSeqN() throws Exception { byte[] bytes = Files.readAllBytes(testClassPath); - ClassModel cm = Classfile.parse(bytes); + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); assertEquals(invoke(bytes), "foo"); - assertEquals(invoke(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo"); - assertEquals(invoke(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux"); - assertEquals(invoke(cm.transform(transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz"); + assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo"); + assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux"); + assertEquals(invoke(cc.transform(cm, transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz"); } public static class TestClass { diff --git a/test/jdk/jdk/classfile/Utf8EntryTest.java b/test/jdk/jdk/classfile/Utf8EntryTest.java index c13d27d39b2..70c754e8e2a 100644 --- a/test/jdk/jdk/classfile/Utf8EntryTest.java +++ b/test/jdk/jdk/classfile/Utf8EntryTest.java @@ -83,7 +83,7 @@ class Utf8EntryTest { void testParse(String s) { byte[] classfile = createClassfile(s); - ClassModel cm = Classfile.parse(classfile); + ClassModel cm = Classfile.of().parse(classfile); StringEntry se = obtainStringEntry(cm.constantPool()); Utf8Entry utf8Entry = se.utf8(); @@ -163,7 +163,7 @@ class Utf8EntryTest { byte[] classfile = createClassfile(marker); replace(classfile, marker, f); - ClassModel cm = Classfile.parse(classfile); + ClassModel cm = Classfile.of().parse(classfile); StringEntry se = obtainStringEntry(cm.constantPool()); assertThrows(RuntimeException.class, () -> { @@ -197,7 +197,7 @@ class Utf8EntryTest { } static byte[] createClassfile(String s) { - return Classfile.build(ClassDesc.of("C"), + return Classfile.of().build(ClassDesc.of("C"), clb -> clb.withMethod("m", MethodTypeDesc.of(CD_void), 0, mb -> mb.withCode(cb -> cb.constantInstruction(s) .returnInstruction(VoidType)))); diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index 2bfaae3c99a..4b88e6969fc 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -53,7 +53,7 @@ class VerifierSelfTest { .flatMap(p -> p) .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> { try { - Classfile.parse(path).verify(null); + Classfile.of().parse(path).verify(null); } catch (IOException e) { throw new AssertionError(e); } @@ -63,9 +63,10 @@ class VerifierSelfTest { @Test void testFailedDump() throws IOException { Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class"); - var classModel = Classfile.parse(path, Classfile.Option.classHierarchyResolver( + var cc = Classfile.of(Classfile.ClassHierarchyResolverOption.of( className -> ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null))); - byte[] brokenClassBytes = classModel.transform( + var classModel = cc.parse(path); + byte[] brokenClassBytes = cc.transform(classModel, (clb, cle) -> { if (cle instanceof MethodModel mm) { clb.transformMethod(mm, (mb, me) -> { @@ -80,7 +81,7 @@ class VerifierSelfTest { clb.with(cle); }); StringBuilder sb = new StringBuilder(); - if (Classfile.parse(brokenClassBytes).verify(sb::append).isEmpty()) { + if (Classfile.of().parse(brokenClassBytes).verify(sb::append).isEmpty()) { throw new AssertionError("expected verification failure"); } String output = sb.toString(); diff --git a/test/jdk/jdk/classfile/WriteTest.java b/test/jdk/jdk/classfile/WriteTest.java index 6402c3d3775..dd333941559 100644 --- a/test/jdk/jdk/classfile/WriteTest.java +++ b/test/jdk/jdk/classfile/WriteTest.java @@ -52,7 +52,7 @@ class WriteTest { @Test void testJavapWrite() { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb @@ -94,7 +94,7 @@ class WriteTest { @Test void testPrimitiveWrite() { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC) .with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .withMethod("", MethodTypeDesc.of(CD_void), 0, mb -> mb diff --git a/test/jdk/jdk/classfile/examples/AnnotationsExamples.java b/test/jdk/jdk/classfile/examples/AnnotationsExamples.java index e371d559f50..56a1f741d12 100644 --- a/test/jdk/jdk/classfile/examples/AnnotationsExamples.java +++ b/test/jdk/jdk/classfile/examples/AnnotationsExamples.java @@ -49,7 +49,7 @@ public class AnnotationsExamples { public byte[] addAnno(ClassModel m) { // @@@ Not correct List annos = List.of(Annotation.of(ClassDesc.of("java.lang.FunctionalInterface"))); - return m.transform(ClassTransform.endHandler(cb -> cb.with(RuntimeVisibleAnnotationsAttribute.of(annos)))); + return Classfile.of().transform(m, ClassTransform.endHandler(cb -> cb.with(RuntimeVisibleAnnotationsAttribute.of(annos)))); } /** @@ -73,9 +73,10 @@ public class AnnotationsExamples { if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) { RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get(); + var cc = Classfile.of(); for (Annotation ann : a.annotations()) { if (ann.className().stringValue().equals("Ljava/lang/annotation/Documented;")) { - m2 = Classfile.parse(m.transform(SWAP_ANNO_TRANSFORM)); + m2 = cc.parse(cc.transform(m, SWAP_ANNO_TRANSFORM)); } } } @@ -116,9 +117,10 @@ public class AnnotationsExamples { if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) { RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get(); + var cc = Classfile.of(); for (Annotation ann : a.annotations()) { if (ann.className().stringValue().equals("Ljava/lang/FunctionalInterface;")) { - m2 = Classfile.parse(m.transform((cb, ce) -> { + m2 = cc.parse(cc.transform(m, (cb, ce) -> { if (ce instanceof RuntimeVisibleAnnotationsAttribute ra) { var oldAnnos = ra.annotations(); List newAnnos = new ArrayList<>(oldAnnos.size() + 1); @@ -144,7 +146,7 @@ public class AnnotationsExamples { } public byte[] viaEndHandlerClassBuilderEdition(ClassModel m) { - return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() { + return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() { boolean found = false; @Override @@ -171,7 +173,7 @@ public class AnnotationsExamples { } public byte[] viaEndHandlerClassTransformEdition(ClassModel m) { - return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() { + return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() { boolean found = false; @Override diff --git a/test/jdk/jdk/classfile/examples/ExampleGallery.java b/test/jdk/jdk/classfile/examples/ExampleGallery.java index 4975fe064c5..7b48c0ca0b1 100644 --- a/test/jdk/jdk/classfile/examples/ExampleGallery.java +++ b/test/jdk/jdk/classfile/examples/ExampleGallery.java @@ -64,7 +64,7 @@ import jdk.internal.classfile.instruction.InvokeInstruction; */ public class ExampleGallery { public byte[] changeClassVersion(ClassModel cm) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case ClassfileVersion cv -> cb.withVersion(57, 0); default -> cb.with(ce); @@ -73,7 +73,7 @@ public class ExampleGallery { } public byte[] incrementClassVersion(ClassModel cm) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case ClassfileVersion cv -> cb.withVersion(cv.majorVersion() + 1, 0); default -> cb.with(ce); @@ -82,7 +82,7 @@ public class ExampleGallery { } public byte[] changeSuperclass(ClassModel cm, ClassDesc superclass) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case Superclass sc -> cb.withSuperclass(superclass); default -> cb.with(ce); @@ -91,11 +91,11 @@ public class ExampleGallery { } public byte[] overrideSuperclass(ClassModel cm, ClassDesc superclass) { - return cm.transform(ClassTransform.endHandler(cb -> cb.withSuperclass(superclass))); + return Classfile.of().transform(cm, ClassTransform.endHandler(cb -> cb.withSuperclass(superclass))); } public byte[] removeInterface(ClassModel cm, String internalName) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case Interfaces i -> cb.withInterfaces(i.interfaces().stream() .filter(e -> !e.asInternalName().equals(internalName)) @@ -106,7 +106,7 @@ public class ExampleGallery { } public byte[] addInterface(ClassModel cm, ClassDesc newIntf) { - return cm.transform(ClassTransform.ofStateful(() -> new ClassTransform() { + return Classfile.of().transform(cm, ClassTransform.ofStateful(() -> new ClassTransform() { boolean seen = false; @Override @@ -135,7 +135,7 @@ public class ExampleGallery { } public byte[] addInterface1(ClassModel cm, ClassDesc newIntf) { - return cm.transform(ClassTransform.ofStateful(() -> new ClassTransform() { + return Classfile.of().transform(cm, ClassTransform.ofStateful(() -> new ClassTransform() { Interfaces interfaces; @Override @@ -162,11 +162,11 @@ public class ExampleGallery { } public byte[] removeSignature(ClassModel cm) { - return cm.transform(ClassTransform.dropping(e -> e instanceof SignatureAttribute)); + return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof SignatureAttribute)); } public byte[] changeSignature(ClassModel cm) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case SignatureAttribute sa -> { String result = sa.signature().stringValue(); @@ -178,7 +178,7 @@ public class ExampleGallery { } public byte[] setSignature(ClassModel cm) { - return cm.transform(ClassTransform.dropping(e -> e instanceof SignatureAttribute) + return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof SignatureAttribute) .andThen(ClassTransform.endHandler(b -> b.with(SignatureAttribute.of( ClassSignature.of( ClassTypeSig.of(ClassDesc.of("impl.Fox"), @@ -189,16 +189,16 @@ public class ExampleGallery { // @@@ strip annos (class, all) public byte[] stripFields(ClassModel cm, Predicate filter) { - return cm.transform(ClassTransform.dropping(e -> e instanceof FieldModel fm + return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof FieldModel fm && filter.test(fm.fieldName().stringValue()))); } public byte[] addField(ClassModel cm) { - return cm.transform(ClassTransform.endHandler(cb -> cb.withField("cool", ClassDesc.ofDescriptor("(I)D"), Classfile.ACC_PUBLIC))); + return Classfile.of().transform(cm, ClassTransform.endHandler(cb -> cb.withField("cool", ClassDesc.ofDescriptor("(I)D"), Classfile.ACC_PUBLIC))); } public byte[] changeFieldSig(ClassModel cm) { - return cm.transform(ClassTransform.transformingFields((fb, fe) -> { + return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> { if (fe instanceof SignatureAttribute sa) fb.with(SignatureAttribute.of(Signature.parseFrom(sa.signature().stringValue().replace("this/", "that/")))); else @@ -207,7 +207,7 @@ public class ExampleGallery { } public byte[] changeFieldFlags(ClassModel cm) { - return cm.transform(ClassTransform.transformingFields((fb, fe) -> { + return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> { switch (fe) { case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~Classfile.ACC_PUBLIC & ~Classfile.ACC_PROTECTED)); default -> fb.with(fe); @@ -216,7 +216,7 @@ public class ExampleGallery { } public byte[] addException(ClassModel cm, ClassDesc ex) { - return cm.transform(ClassTransform.transformingMethods( + return Classfile.of().transform(cm, ClassTransform.transformingMethods( MethodTransform.ofStateful(() -> new MethodTransform() { ExceptionsAttribute attr; @@ -260,11 +260,11 @@ public class ExampleGallery { } }); - return cm.transform(ClassTransform.transformingMethodBodies(transform)); + return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies(transform)); } public byte[] addInstrumentationBeforeInvoke(ClassModel cm) { - return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> { + return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> { switch (codeE) { case InvokeInstruction i -> { codeB.nopInstruction(); @@ -276,7 +276,7 @@ public class ExampleGallery { } public byte[] replaceIntegerConstant(ClassModel cm) { - return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> { + return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> { switch (codeE) { case ConstantInstruction ci -> { if (ci.constantValue() instanceof Integer i) codeB.constantInstruction(i + 1); diff --git a/test/jdk/jdk/classfile/examples/ExperimentalTransformExamples.java b/test/jdk/jdk/classfile/examples/ExperimentalTransformExamples.java index c26e8622b26..417b048cf72 100644 --- a/test/jdk/jdk/classfile/examples/ExperimentalTransformExamples.java +++ b/test/jdk/jdk/classfile/examples/ExperimentalTransformExamples.java @@ -54,7 +54,7 @@ public class ExperimentalTransformExamples { }; public byte[] deleteAnnotations(ClassModel cm) { - return cm.transform((cb, ce) -> { + return Classfile.of().transform(cm, (cb, ce) -> { switch (ce) { case MethodModel m -> cb.transformMethod(m, dropMethodAnnos); case FieldModel f -> cb.transformField(f, dropFieldAnnos); diff --git a/test/jdk/jdk/classfile/examples/ModuleExamples.java b/test/jdk/jdk/classfile/examples/ModuleExamples.java index 8b97b7f8770..9bfedc8d10c 100644 --- a/test/jdk/jdk/classfile/examples/ModuleExamples.java +++ b/test/jdk/jdk/classfile/examples/ModuleExamples.java @@ -53,7 +53,7 @@ public class ModuleExamples { private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/")); public void examineModule() throws IOException { - ClassModel cm = Classfile.parse(JRT.getPath("modules/java.base/module-info.class")); + ClassModel cm = Classfile.of().parse(JRT.getPath("modules/java.base/module-info.class")); System.out.println("Is JVMS $4.7 compatible module-info: " + cm.isModuleInfo()); ModuleAttribute ma = cm.findAttribute(Attributes.MODULE).orElseThrow(); @@ -78,7 +78,8 @@ public class ModuleExamples { }); // Build it - byte[] moduleInfo = Classfile.buildModule(ModuleAttribute.of(moduleName, handler), clb -> { + var cc = Classfile.of(); + byte[] moduleInfo = cc.buildModule(ModuleAttribute.of(moduleName, handler), clb -> { // Add an annotation to the module clb.with(RuntimeVisibleAnnotationsAttribute.of(Annotation.of(ClassDesc.ofDescriptor("Ljava/lang/Deprecated;"), @@ -87,7 +88,7 @@ public class ModuleExamples { }); // Examine it - ClassModel mm = Classfile.parse(moduleInfo); + ClassModel mm = cc.parse(moduleInfo); System.out.println("Is module info?: " + mm.isModuleInfo()); } } diff --git a/test/jdk/jdk/classfile/examples/TransformExamples.java b/test/jdk/jdk/classfile/examples/TransformExamples.java index d944955b70b..9bade619df5 100644 --- a/test/jdk/jdk/classfile/examples/TransformExamples.java +++ b/test/jdk/jdk/classfile/examples/TransformExamples.java @@ -28,6 +28,7 @@ * @summary Testing Classfile TransformExamples compilation. * @compile TransformExamples.java */ +import jdk.internal.classfile.Classfile; import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.FieldModel; @@ -39,18 +40,18 @@ import jdk.internal.classfile.Attribute; */ public class TransformExamples { public byte[] noop(ClassModel cm) { - return cm.transform(ClassTransform.ACCEPT_ALL); + return Classfile.of().transform(cm, ClassTransform.ACCEPT_ALL); } public byte[] deleteAllMethods(ClassModel cm) { - return cm.transform((b, e) -> { + return Classfile.of().transform(cm, (b, e) -> { if (!(e instanceof MethodModel)) b.with(e); }); } public byte[] deleteFieldsWithDollarInName(ClassModel cm) { - return cm.transform((b, e) -> + return Classfile.of().transform(cm, (b, e) -> { if (!(e instanceof FieldModel fm && fm.fieldName().stringValue().contains("$"))) b.with(e); @@ -58,14 +59,14 @@ public class TransformExamples { } public byte[] deleteAttributes(ClassModel cm) { - return cm.transform((b, e) -> { + return Classfile.of().transform(cm, (b, e) -> { if (!(e instanceof Attribute)) b.with(e); }); } public byte[] keepMethodsAndFields(ClassModel cm) { - return cm.transform((b, e) -> { + return Classfile.of().transform(cm, (b, e) -> { if (e instanceof MethodModel || e instanceof FieldModel) b.with(e); }); diff --git a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java index 0c21d17b9c9..bbb6f8c1f5a 100644 --- a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java +++ b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java @@ -41,7 +41,7 @@ class RebuildingTransformation { static private Random pathSwitch = new Random(1234); static byte[] transform(ClassModel clm) { - return Classfile.build(clm.thisClass().asSymbol(), List.of(Classfile.Option.generateStackmap(false)), clb -> { + return Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS).build(clm.thisClass().asSymbol(), clb -> { for (var cle : clm) { switch (cle) { case AccessFlags af -> clb.withFlags(af.flagsMask()); diff --git a/test/jdk/jdk/classfile/helpers/Transforms.java b/test/jdk/jdk/classfile/helpers/Transforms.java index 7dc043acdd8..2e5c9782f50 100644 --- a/test/jdk/jdk/classfile/helpers/Transforms.java +++ b/test/jdk/jdk/classfile/helpers/Transforms.java @@ -115,7 +115,7 @@ public class Transforms { return bs; }), BUILD_FROM_SCRATCH(bytes -> { - return RebuildingTransformation.transform(Classfile.parse(bytes)); + return RebuildingTransformation.transform(Classfile.of().parse(bytes)); }), SHARED_1(true, oneLevelNoop), SHARED_2(true, twoLevelNoop), @@ -129,8 +129,8 @@ public class Transforms { UNSHARED_1(false, oneLevelNoop), UNSHARED_2(false, twoLevelNoop), UNSHARED_3(false, threeLevelNoop), - SHARED_3_NO_STACKMAP(true, threeLevelNoop, Classfile.Option.generateStackmap(false)), - SHARED_3_NO_DEBUG(true, threeLevelNoop, Classfile.Option.processDebug(false), Classfile.Option.processLineNumbers(false)), + SHARED_3_NO_STACKMAP(true, threeLevelNoop, Classfile.StackMapsOption.DROP_STACK_MAPS), + SHARED_3_NO_DEBUG(true, threeLevelNoop, Classfile.DebugElementsOption.DROP_DEBUG, Classfile.LineNumbersOption.DROP_LINE_NUMBERS), ASM_1(bytes -> { ClassReader cr = new ClassReader(bytes); jdk.internal.org.objectweb.asm.ClassWriter cw = new jdk.internal.org.objectweb.asm.ClassWriter(cr, jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES); @@ -164,20 +164,20 @@ public class Transforms { return cw.toByteArray(); }), CLASS_REMAPPER(bytes -> - ClassRemapper.of(Map.of()).remapClass(Classfile.parse(bytes))); + ClassRemapper.of(Map.of()).remapClass(Classfile.of(), Classfile.of().parse(bytes))); // Need ASM, LOW_UNSHARED public final UnaryOperator transform; public final boolean shared; public final ClassTransform classTransform; - public final Classfile.Option[] options; + public final Classfile cc; NoOpTransform(UnaryOperator transform) { this.transform = transform; classTransform = null; shared = false; - options = new Classfile.Option[0]; + cc = Classfile.of(); } NoOpTransform(boolean shared, @@ -185,19 +185,20 @@ public class Transforms { Classfile.Option... options) { this.shared = shared; this.classTransform = classTransform; - this.options = shared - ? options - : Stream.concat(Stream.of(options), Stream.of(Classfile.Option.constantPoolSharing(false))).toArray(Classfile.Option[]::new); - this.transform = bytes -> Classfile.parse(bytes, this.options).transform(classTransform); + this.cc = Classfile.of( + shared + ? options + : Stream.concat(Stream.of(options), Stream.of(Classfile.ConstantPoolSharingOption.NEW_POOL)).toArray(Classfile.Option[]::new)); + this.transform = bytes -> cc.transform(cc.parse(bytes), classTransform); } public Optional classRecord(byte[] bytes) throws IOException { return switch (this) { - case ARRAYCOPY -> Optional.of(ClassRecord.ofClassModel(Classfile.parse(bytes))); + case ARRAYCOPY -> Optional.of(ClassRecord.ofClassModel(Classfile.of().parse(bytes))); case SHARED_1, SHARED_2, SHARED_3, UNSHARED_1, UNSHARED_2, UNSHARED_3, BUILD_FROM_SCRATCH - -> Optional.of(ClassRecord.ofClassModel(Classfile.parse(bytes), ClassRecord.CompatibilityFilter.By_ClassBuilder)); + -> Optional.of(ClassRecord.ofClassModel(Classfile.of().parse(bytes), ClassRecord.CompatibilityFilter.By_ClassBuilder)); default -> Optional.empty(); }; } @@ -211,8 +212,9 @@ public class Transforms { return cw.toByteArray(); }), NOP_SHARED(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform((cb, ce) -> { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, (cb, ce) -> { if (ce instanceof MethodModel mm) { cb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel xm) { @@ -251,8 +253,9 @@ public class Transforms { return cw.toByteArray(); }), HIGH_SHARED_ADD_FIELD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform(new ClassTransform() { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, new ClassTransform() { @Override public void accept(ClassBuilder builder, ClassElement element) { builder.with(element); @@ -265,8 +268,9 @@ public class Transforms { }); }), HIGH_UNSHARED_ADD_FIELD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return Classfile.build(cm.thisClass().asSymbol(), + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.build(cm.thisClass().asSymbol(), cb -> { cm.forEachElement(cb); cb.withField("argleBargleWoogaWooga", ConstantDescs.CD_int, b -> { }); @@ -287,15 +291,17 @@ public class Transforms { return cw.toByteArray(); }), HIGH_SHARED_DEL_METHOD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform((builder, element) -> { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, (builder, element) -> { if (!(element instanceof MethodModel mm)) builder.with(element); }); }), HIGH_UNSHARED_DEL_METHOD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return Classfile.build(cm.thisClass().asSymbol(), + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.build(cm.thisClass().asSymbol(), cb -> { cm.forEachElement(element -> { if (element instanceof MethodModel mm diff --git a/test/lib/jdk/test/lib/util/ModuleInfoWriter.java b/test/lib/jdk/test/lib/util/ModuleInfoWriter.java index 91a96334371..b241b66555d 100644 --- a/test/lib/jdk/test/lib/util/ModuleInfoWriter.java +++ b/test/lib/jdk/test/lib/util/ModuleInfoWriter.java @@ -87,7 +87,7 @@ public final class ModuleInfoWriter { ModuleResolution mres, ModuleTarget target) { //using low-level module building to avoid validation in ModuleDesc and allow invalid names - return Classfile.build(ClassDesc.of("module-info"), clb -> { + return Classfile.of().build(ClassDesc.of("module-info"), clb -> { clb.withFlags(AccessFlag.MODULE); var cp = clb.constantPool(); clb.with(ModuleAttribute.of(cp.moduleEntry(cp.utf8Entry(md.name())), mb -> { diff --git a/test/micro/org/openjdk/bench/jdk/classfile/AdHocAdapt.java b/test/micro/org/openjdk/bench/jdk/classfile/AdHocAdapt.java index 5213f7ca8ff..a4ea393a635 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/AdHocAdapt.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/AdHocAdapt.java @@ -57,7 +57,8 @@ public class AdHocAdapt extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void transform(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) - bh.consume(Classfile.parse(bytes).transform(transform.transform)); + bh.consume(cc.transform(cc.parse(bytes), transform.transform)); } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/ClassfileBenchmark.java b/test/micro/org/openjdk/bench/jdk/classfile/ClassfileBenchmark.java new file mode 100644 index 00000000000..1edc1bd9fba --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/classfile/ClassfileBenchmark.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.classfile; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.ClassTransform; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.CodeBuilder; +import jdk.internal.classfile.CodeElement; +import jdk.internal.classfile.CodeTransform; +import jdk.internal.classfile.CompoundElement; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; + +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * ClassfileBenchmark + */ +@Warmup(iterations = 3) +@Measurement(iterations = 5) +@Fork(value = 1, jvmArgsAppend = { + "--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED"}) + +@State(Scope.Benchmark) +public class ClassfileBenchmark { + private byte[] benchBytes; + private ClassModel benchModel; + private Classfile sharedCP, newCP; + private ClassTransform threeLevelNoop; + private ClassTransform addNOP; + + @Setup + public void setup() throws IOException { + benchBytes = Files.readAllBytes( + FileSystems.getFileSystem(URI.create("jrt:/")) + .getPath("modules/java.base/java/util/AbstractMap.class")); + sharedCP = Classfile.of(); + newCP = Classfile.of(Classfile.ConstantPoolSharingOption.NEW_POOL); + benchModel = Classfile.of().parse(benchBytes); + threeLevelNoop = ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL); + addNOP = ClassTransform.transformingMethodBodies(new CodeTransform() { + @Override + public void atStart(CodeBuilder cob) { + cob.nop(); + } + @Override + public void accept(CodeBuilder cob, CodeElement coe) { + cob.with(coe); + } + }); + //expand the model + consume(benchModel); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void parse(Blackhole bh) { + consume(sharedCP.parse(benchBytes)); + } + + private static void consume(CompoundElement parent) { + parent.forEach(e -> { + if (e instanceof CompoundElement ce) consume(ce); + }); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void transformWithSharedCP(Blackhole bh) { + bh.consume(sharedCP.transform(benchModel, threeLevelNoop)); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void transformWithNewCP(Blackhole bh) { + bh.consume(newCP.transform(benchModel, threeLevelNoop)); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void transformWithAddedNOP(Blackhole bh) { + bh.consume(sharedCP.transform(benchModel, addNOP)); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java b/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java index e11edf16edc..75729ef69fc 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java @@ -40,6 +40,7 @@ import jdk.internal.classfile.constantpool.ConstantPoolBuilder; import jdk.internal.classfile.impl.AbstractPseudoInstruction; import jdk.internal.classfile.impl.CodeImpl; import jdk.internal.classfile.impl.LabelContext; +import jdk.internal.classfile.impl.ClassfileImpl; import jdk.internal.classfile.impl.SplitConstantPool; import jdk.internal.classfile.impl.StackMapGenerator; import org.openjdk.jmh.annotations.Benchmark; @@ -78,13 +79,15 @@ public class GenerateStackMaps { List data; Iterator it; GenData d; + Classfile cc; @Setup(Level.Trial) public void setup() throws IOException { + cc = Classfile.of(); data = new ArrayList<>(); Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> { if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try { - var clm = Classfile.parse(p); + var clm = cc.parse(p); var thisCls = clm.thisClass().asSymbol(); var cp = new SplitConstantPool((ClassReader)clm.constantPool()); for (var m : clm.methods()) { @@ -120,6 +123,7 @@ public class GenerateStackMaps { d.isStatic(), d.bytecode().rewind(), (SplitConstantPool)d.constantPool(), + (ClassfileImpl)cc, d.handlers()); } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/ParseOptions.java b/test/micro/org/openjdk/bench/jdk/classfile/ParseOptions.java index 42a9f4d9ff4..77eb03c14f3 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/ParseOptions.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/ParseOptions.java @@ -41,27 +41,30 @@ public class ParseOptions extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void transformNoDebug(Blackhole bh) { + var cc = Classfile.of(Classfile.DebugElementsOption.DROP_DEBUG); for (byte[] aClass : classes) { - ClassModel cm = Classfile.parse(aClass, Classfile.Option.processDebug(false)); - bh.consume(cm.transform(threeLevelNoop)); + ClassModel cm = cc.parse(aClass); + bh.consume(cc.transform(cm, threeLevelNoop)); } } @Benchmark @BenchmarkMode(Mode.Throughput) public void transformNoStackmap(Blackhole bh) { + var cc = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS); for (byte[] aClass : classes) { - ClassModel cm = Classfile.parse(aClass, Classfile.Option.generateStackmap(false)); - bh.consume(cm.transform(threeLevelNoop)); + ClassModel cm = cc.parse(aClass); + bh.consume(cc.transform(cm, threeLevelNoop)); } } @Benchmark @BenchmarkMode(Mode.Throughput) public void transformNoLineNumbers(Blackhole bh) { + var cc = Classfile.of(Classfile.LineNumbersOption.DROP_LINE_NUMBERS); for (byte[] aClass : classes) { - ClassModel cm = Classfile.parse(aClass, Classfile.Option.processLineNumbers(false)); - bh.consume(cm.transform(threeLevelNoop)); + ClassModel cm = cc.parse(aClass); + bh.consume(cc.transform(cm, threeLevelNoop)); } } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/ReadDeep.java b/test/micro/org/openjdk/bench/jdk/classfile/ReadDeep.java index 81ed7b53462..716859d5049 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/ReadDeep.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/ReadDeep.java @@ -99,9 +99,10 @@ public class ReadDeep extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void jdkElementsCountLoads(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) { int[] count = new int[1]; - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); cm.forEachElement(ce -> { if (ce instanceof MethodModel mm) { mm.forEachElement(me -> { @@ -122,8 +123,9 @@ public class ReadDeep extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void jdkElementsDeepIterate(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) { - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); bh.consume(iterateAll(cm)); } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/ReadMetadata.java b/test/micro/org/openjdk/bench/jdk/classfile/ReadMetadata.java index 8b254004ab0..79a0545b5af 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/ReadMetadata.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/ReadMetadata.java @@ -68,8 +68,9 @@ public class ReadMetadata extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void jdkReadName(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) { - bh.consume(Classfile.parse(bytes).thisClass().asInternalName()); + bh.consume(cc.parse(bytes).thisClass().asInternalName()); } } @@ -113,9 +114,10 @@ public class ReadMetadata extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void jdkTreeCountFields(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) { int count = 0; - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); for (FieldModel fm : cm.fields()) if (!fm.flags().has(AccessFlag.PUBLIC)) { ++count; @@ -127,9 +129,10 @@ public class ReadMetadata extends AbstractCorpusBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) public void jdkCountFields(Blackhole bh) { + var cc = Classfile.of(); for (byte[] bytes : classes) { int count = 0; - ClassModel cm = Classfile.parse(bytes); + ClassModel cm = cc.parse(bytes); for (ClassElement ce : cm) { if (ce instanceof FieldModel fm) { if (!fm.flags().has(AccessFlag.PUBLIC)) { diff --git a/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java b/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java index c18433deffa..b3126bdddf6 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java @@ -47,27 +47,28 @@ import org.openjdk.jmh.annotations.*; @Measurement(iterations = 4) public class RebuildMethodBodies { - List shared, unshared; - Iterator it1, it2; + Classfile shared, unshared; + List models; + Iterator it; @Setup(Level.Trial) public void setup() throws IOException { - shared = new ArrayList<>(); - unshared = new ArrayList<>(); + shared = Classfile.of( + Classfile.ConstantPoolSharingOption.SHARED_POOL, + Classfile.DebugElementsOption.DROP_DEBUG, + Classfile.LineNumbersOption.DROP_LINE_NUMBERS); + unshared = Classfile.of( + Classfile.ConstantPoolSharingOption.NEW_POOL, + Classfile.DebugElementsOption.DROP_DEBUG, + Classfile.LineNumbersOption.DROP_LINE_NUMBERS); + models = new ArrayList<>(); Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> { if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try { - var clm = Classfile.parse(p, - Classfile.Option.constantPoolSharing(true), - Classfile.Option.processDebug(false), - Classfile.Option.processLineNumbers(false)); - shared.add(clm); - transform(clm); //dry run to expand model and symbols - clm = Classfile.parse(p, - Classfile.Option.constantPoolSharing(false), - Classfile.Option.processDebug(false), - Classfile.Option.processLineNumbers(false)); - unshared.add(clm); - transform(clm); //dry run to expand model and symbols + var clm = shared.parse(p); + models.add(clm); + //dry run to expand model and symbols + transform(shared, clm); + transform(unshared, clm); } catch (IOException e) { throw new RuntimeException(e); } @@ -76,22 +77,22 @@ public class RebuildMethodBodies { @Benchmark public void shared() { - if (it1 == null || !it1.hasNext()) - it1 = shared.iterator(); + if (it == null || !it.hasNext()) + it = models.iterator(); //model and symbols were already expanded, so benchmark is focused more on builder performance - transform(it1.next()); + transform(shared, it.next()); } @Benchmark public void unshared() { - if (it2 == null || !it2.hasNext()) - it2 = unshared.iterator(); + if (it == null || !it.hasNext()) + it = models.iterator(); //model and symbols were already expanded, so benchmark is focused more on builder performance - transform(it2.next()); + transform(unshared, it.next()); } - private static void transform(ClassModel clm) { - clm.transform(ClassTransform.transformingMethodBodies((cob, coe) -> { + private static void transform(Classfile cc, ClassModel clm) { + cc.transform(clm, ClassTransform.transformingMethodBodies((cob, coe) -> { switch (coe) { case FieldInstruction i -> cob.fieldInstruction(i.opcode(), i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol()); diff --git a/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java index 601dba50806..be374563ea7 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java @@ -51,9 +51,10 @@ public class RepeatedModelTraversal { @Setup(Level.Trial) public void setup() throws IOException { models = new ArrayList<>(); + var cc = Classfile.of(); Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util")).forEach(p -> { if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try { - var clm = Classfile.parse(p); + var clm = cc.parse(p); models.add(clm); } catch (IOException e) { throw new RuntimeException(e); diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java b/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java index cd647858a1b..f9cf0ea07de 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java @@ -126,8 +126,8 @@ public class Transforms { UNSHARED_1(false, oneLevelNoop), UNSHARED_2(false, twoLevelNoop), UNSHARED_3(false, threeLevelNoop), - SHARED_3_NO_STACKMAP(true, threeLevelNoop, Classfile.Option.generateStackmap(false)), - SHARED_3_NO_DEBUG(true, threeLevelNoop, Classfile.Option.processDebug(false), Classfile.Option.processLineNumbers(false)), + SHARED_3_NO_STACKMAP(true, threeLevelNoop, Classfile.StackMapsOption.DROP_STACK_MAPS), + SHARED_3_NO_DEBUG(true, threeLevelNoop, Classfile.DebugElementsOption.DROP_DEBUG, Classfile.LineNumbersOption.DROP_LINE_NUMBERS), ASM_1(bytes -> { ClassReader cr = new ClassReader(bytes); jdk.internal.org.objectweb.asm.ClassWriter cw = new jdk.internal.org.objectweb.asm.ClassWriter(cr, jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES); @@ -161,20 +161,20 @@ public class Transforms { return cw.toByteArray(); }), CLASS_REMAPPER(bytes -> - ClassRemapper.of(Map.of()).remapClass(Classfile.parse(bytes))); + ClassRemapper.of(Map.of()).remapClass(Classfile.of(), Classfile.of().parse(bytes))); // Need ASM, LOW_UNSHARED public final UnaryOperator transform; public final boolean shared; public final ClassTransform classTransform; - public final Classfile.Option[] options; + public final Classfile cc; NoOpTransform(UnaryOperator transform) { this.transform = transform; classTransform = null; shared = false; - options = new Classfile.Option[0]; + cc = Classfile.of(); } NoOpTransform(boolean shared, @@ -182,10 +182,11 @@ public class Transforms { Classfile.Option... options) { this.shared = shared; this.classTransform = classTransform; - this.options = shared - ? options - : Stream.concat(Stream.of(options), Stream.of(Classfile.Option.constantPoolSharing(false))).toArray(Classfile.Option[]::new); - this.transform = bytes -> Classfile.parse(bytes, this.options).transform(classTransform); + this.cc = Classfile.of( + shared + ? options + : Stream.concat(Stream.of(options), Stream.of(Classfile.ConstantPoolSharingOption.NEW_POOL)).toArray(Classfile.Option[]::new)); + this.transform = bytes -> cc.transform(cc.parse(bytes), classTransform); } } @@ -197,8 +198,9 @@ public class Transforms { return cw.toByteArray(); }), NOP_SHARED(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform((cb, ce) -> { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, (cb, ce) -> { if (ce instanceof MethodModel mm) { cb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel xm) { @@ -237,8 +239,9 @@ public class Transforms { return cw.toByteArray(); }), HIGH_SHARED_ADD_FIELD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform(new ClassTransform() { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, new ClassTransform() { @Override public void accept(ClassBuilder builder, ClassElement element) { builder.with(element); @@ -251,8 +254,9 @@ public class Transforms { }); }), HIGH_UNSHARED_ADD_FIELD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return Classfile.build(cm.thisClass().asSymbol(), + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.build(cm.thisClass().asSymbol(), cb -> { cm.forEachElement(cb); cb.withField("argleBargleWoogaWooga", ConstantDescs.CD_int, b -> { }); @@ -273,15 +277,17 @@ public class Transforms { return cw.toByteArray(); }), HIGH_SHARED_DEL_METHOD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return cm.transform((builder, element) -> { + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.transform(cm, (builder, element) -> { if (!(element instanceof MethodModel mm)) builder.with(element); }); }), HIGH_UNSHARED_DEL_METHOD(bytes -> { - ClassModel cm = Classfile.parse(bytes); - return Classfile.build(cm.thisClass().asSymbol(), + var cc = Classfile.of(); + ClassModel cm = cc.parse(bytes); + return cc.build(cm.thisClass().asSymbol(), cb -> { cm.forEachElement(element -> { if (element instanceof MethodModel mm diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index 485e61e39ed..3b3e19b4c75 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -149,7 +149,7 @@ public class Write { @BenchmarkMode(Mode.Throughput) public byte[] jdkTree() { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) @@ -197,7 +197,7 @@ public class Write { @BenchmarkMode(Mode.Throughput) public byte[] jdkTreePrimitive() { - byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { + byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))