Skip to content

8355658: Allow transforms to run on elements generated by a builder #24908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,8 @@

import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Pair;

import java.nio.file.DirectoryStream;
import java.util.Optional;
import java.util.function.Consumer;
Expand Down Expand Up @@ -966,14 +964,12 @@ private void addAttributes(FieldDescription desc, FieldBuilder builder) {
}
}

@SuppressWarnings("unchecked")
private void addGenericAttributes(FeatureDescription desc, ClassFileBuilder<?, ?> builder) {
addGenericAttributes(desc, (Consumer<? super Attribute<?>>) builder, builder.constantPool());
@SuppressWarnings({"unchecked", "rawtypes"})
private void addGenericAttributes(FeatureDescription desc, ClassFileBuilder builder) {
addGenericAttributes(desc, (Consumer<Attribute<?>>) builder, builder.constantPool());
}

private void addGenericAttributes(FeatureDescription desc, Consumer<? super Attribute<?>> sink, ConstantPoolBuilder cpb) {
@SuppressWarnings("unchecked")
var builder = (Consumer<Attribute<?>>) sink;
private void addGenericAttributes(FeatureDescription desc, Consumer<Attribute<?>> builder, ConstantPoolBuilder cpb) {
if (desc.deprecated) {
builder.accept(DeprecatedAttribute.of());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import jdk.internal.classfile.impl.AccessFlagsImpl;
import jdk.internal.classfile.impl.ChainedClassBuilder;
import jdk.internal.classfile.impl.DirectClassBuilder;
import jdk.internal.classfile.impl.TransformImpl;
import jdk.internal.classfile.impl.Util;

/**
Expand All @@ -57,7 +58,7 @@
* @since 24
*/
public sealed interface ClassBuilder
extends ClassFileBuilder<ClassElement, ClassBuilder>
extends ClassFileBuilder<ClassTransform, ClassElement, ClassBuilder>
permits ChainedClassBuilder, DirectClassBuilder {

/**
Expand Down Expand Up @@ -360,4 +361,18 @@ default ClassBuilder withMethodBody(String name,
* @see MethodTransform
*/
ClassBuilder transformMethod(MethodModel method, MethodTransform transform);

// Implementations

/**
* @since 25
*/
@Override
default ClassBuilder transforming(ClassTransform transform, Consumer<? super ClassBuilder> handler) {
var resolved = TransformImpl.resolve(transform, this);
resolved.startHandler().run();
handler.accept(new ChainedClassBuilder(this, resolved.consumer()));
resolved.endHandler().run();
return this;
}
}
15 changes: 6 additions & 9 deletions src/java.base/share/classes/java/lang/classfile/ClassFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@

import java.io.IOException;
import java.lang.classfile.AttributeMapper.AttributeStability;
import java.lang.classfile.attribute.CharacterRangeInfo;
import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.attribute.LocalVariableInfo;
import java.lang.classfile.attribute.LocalVariableTypeInfo;
import java.lang.classfile.attribute.ModuleAttribute;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.attribute.UnknownAttribute;
Expand Down Expand Up @@ -680,8 +677,8 @@ default void buildModuleTo(Path path,
* This is named {@code transformClass} instead of {@code transform} for
* consistency with {@link ClassBuilder#transformField}, {@link
* ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
* and to distinguish from {@link ClassFileBuilder#transform}, which is
* more generic and powerful.
* and to distinguish from {@link ClassFileBuilder#transform} and {@link
* ClassFileBuilder#transforming}, which are more generic and powerful.
*
* @param model the class model to transform
* @param transform the transform
Expand All @@ -704,8 +701,8 @@ default byte[] transformClass(ClassModel model, ClassTransform transform) {
* This is named {@code transformClass} instead of {@code transform} for
* consistency with {@link ClassBuilder#transformField}, {@link
* ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
* and to distinguish from {@link ClassFileBuilder#transform}, which is
* more generic and powerful.
* and to distinguish from {@link ClassFileBuilder#transform} and {@link
* ClassFileBuilder#transforming}, which are more generic and powerful.
*
* @param model the class model to transform
* @param newClassName new class name
Expand Down Expand Up @@ -736,8 +733,8 @@ default byte[] transformClass(ClassModel model, ClassDesc newClassName, ClassTra
* This is named {@code transformClass} instead of {@code transform} for
* consistency with {@link ClassBuilder#transformField}, {@link
* ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
* and to distinguish from {@link ClassFileBuilder#transform}, which is
* more generic and powerful.
* and to distinguish from {@link ClassFileBuilder#transform} and {@link
* ClassFileBuilder#transforming}, which are more generic and powerful.
*
* @param model the class model to transform
* @param newClassName new class name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.constant.ClassDesc;
import java.util.Objects;
import java.util.function.Consumer;

import jdk.internal.classfile.impl.TransformImpl;
Expand Down Expand Up @@ -59,12 +60,13 @@
*
* @param <E> the member element type
* @param <B> the self type of this builder
* @param <C> the type of transform that runs on this builder
* @see CompoundElement
* @see ClassFileTransform
* @sealedGraph
* @since 24
*/
public sealed interface ClassFileBuilder<E extends ClassFileElement, B extends ClassFileBuilder<E, B>>
public sealed interface ClassFileBuilder<C extends ClassFileTransform<C, E, B>, E extends ClassFileElement, B extends ClassFileBuilder<C, E, B>>
extends Consumer<E> permits ClassBuilder, FieldBuilder, MethodBuilder, CodeBuilder {

/**
Expand Down Expand Up @@ -106,8 +108,12 @@ default void accept(E e) {
* well as this builder for building the structure. The transform is free
* to preserve, remove, or replace elements as it sees fit.
* <p>
* A {@linkplain #transforming(ClassFileTransform, Consumer) handler-based}
* version behaves similarly; the elements built by the handler are passed
* instead of elements from an existing structure.
* <p>
* A builder can run multiple transforms against different compound
* structures, integrating member elements of different origins.
* structures and handlers, integrating member elements of different origins.
*
* @apiNote
* Many subinterfaces have methods like {@link ClassBuilder#transformMethod}
Expand All @@ -122,8 +128,15 @@ default void accept(E e) {
* @param transform the transform to apply
* @return this builder
* @see ClassFileTransform
* @see #transforming(ClassFileTransform, Consumer)
*/
default B transform(CompoundElement<E> model, ClassFileTransform<?, E, B> transform) {
default B transform(CompoundElement<E> model, C transform) {
Objects.requireNonNull(model);
Objects.requireNonNull(transform);

// This can be as simple as:
// return transform(Util.writingAll(model), transform);
// but this version saves an additional builder
@SuppressWarnings("unchecked")
B builder = (B) this;
var resolved = TransformImpl.resolve(transform, builder);
Expand All @@ -132,4 +145,42 @@ default B transform(CompoundElement<E> model, ClassFileTransform<?, E, B> transf
resolved.endHandler().run();
return builder;
}

/**
* Applies a transform to a structure built by a handler, directing results
* to this builder. The builder passed to the handler is initialized with
* the same required arguments as this builder.
* <p>
* The transform will receive each element built by the handler, as well
* as this builder for building the structure. The transform is free
* to preserve, remove, or replace elements as it sees fit.
* <p>
* A {@linkplain #transform(CompoundElement, ClassFileTransform)
* structure-based} version behaves similarly; the elements from an existing
* source are passed instead of elements built by a handler.
* <p>
* A builder can run multiple transforms against different compound
* structures and handlers, integrating member elements of different origins.
*
* @apiNote
* Many subinterfaces have methods like {@link ClassBuilder#transformMethod}
* or {@link MethodBuilder#transformCode}. However, calling them is
* fundamentally different from calling this method: those methods call the
* {@code transform} on the child builders instead of on itself. For
* example, {@code classBuilder.transformMethod} calls {@code
* methodBuilder.transform} with a new method builder instead of calling
* {@code classBuilder.transform} on itself.
* <p>
* If elements are sourced from a {@link CompoundElement}, the {@linkplain
* #transform(CompoundElement, ClassFileTransform) structure-based} version
* may be more efficient.
*
* @param transform the transform to apply
* @param handler the handler to produce elements to be transformed
* @return this builder
* @see ClassFileTransform
* @see #transform(CompoundElement, ClassFileTransform)
* @since 25
*/
B transforming(C transform, Consumer<? super B> handler);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@

import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* A transformation on a {@link CompoundElement} by processing its individual
* member elements and sending the results to a {@link ClassFileBuilder},
* through {@link ClassFileBuilder#transform}. A subtype of {@code
* ClassFileTransform} is defined for each subtype of {@link CompoundElement}
* and {@link ClassFileBuilder}, as shown in the sealed class hierarchy below.
* through {@link ClassFileBuilder#transform} and {@link
* ClassFileBuilder#transforming}. A subtype of {@code ClassFileTransform} is
* defined for each subtype of {@link CompoundElement} and {@link
* ClassFileBuilder}, as shown in the sealed class hierarchy below.
* <p>
* For example, this is a basic transformation of a {@link CodeModel} that
* redirects all calls to static methods in the {@code Foo} class to the {@code
Expand Down Expand Up @@ -85,8 +85,9 @@
* transform the method body of select methods in the class it runs on. This
* allows users to write small transforms and apply to larger scales.
* <p>
* Besides {@link ClassFileBuilder#transform}, there are other methods that
* accepts a transform conveniently, such as {@link ClassFile#transformClass},
* Besides {@link ClassFileBuilder#transform} and {@link
* ClassFileBuilder#transforming}, there are other methods that accepts a
* transform conveniently, such as {@link ClassFile#transformClass},
* {@link ClassBuilder#transformField}, {@link ClassBuilder#transformMethod}, or
* {@link MethodBuilder#transformCode}. They are convenience methods that suit
* the majority of transformation scenarios.
Expand All @@ -101,7 +102,7 @@
public sealed interface ClassFileTransform<
C extends ClassFileTransform<C, E, B>,
E extends ClassFileElement,
B extends ClassFileBuilder<E, B>>
B extends ClassFileBuilder<C, E, B>>
permits ClassTransform, FieldTransform, MethodTransform, CodeTransform {
/**
* Transform an element by taking the appropriate actions on the builder.
Expand Down
24 changes: 11 additions & 13 deletions src/java.base/share/classes/java/lang/classfile/CodeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.lang.constant.MethodTypeDesc;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

Expand Down Expand Up @@ -92,7 +93,7 @@
* @since 24
*/
public sealed interface CodeBuilder
extends ClassFileBuilder<CodeElement, CodeBuilder>
extends ClassFileBuilder<CodeTransform, CodeElement, CodeBuilder>
permits CodeBuilder.BlockCodeBuilder, ChainedCodeBuilder, TerminalCodeBuilder, NonterminalCodeBuilder {

/**
Expand Down Expand Up @@ -150,19 +151,16 @@ public sealed interface CodeBuilder
int allocateLocal(TypeKind typeKind);

/**
* Apply a transform to the code built by a handler, directing results to
* this builder.
* {@inheritDoc}
*
* @apiNote
* This is similar to {@link #transform}, but this does not require the
* code elements to be viewed as a {@link CodeModel} first.
*
* @param transform the transform to apply to the code built by the handler
* @param handler the handler that receives a {@link CodeBuilder} to
* build the code
* @return this builder
*/
default CodeBuilder transforming(CodeTransform transform, Consumer<CodeBuilder> handler) {
* @param transform {@inheritDoc}
* @param handler {@inheritDoc}
* @return {@inheritDoc}
*/ // remove supertype incorrect since
@Override
default CodeBuilder transforming(CodeTransform transform, Consumer<? super CodeBuilder> handler) {
Objects.requireNonNull(handler);
Objects.requireNonNull(transform);
var resolved = TransformImpl.resolve(transform, this);
resolved.startHandler().run();
handler.accept(new ChainedCodeBuilder(this, resolved.consumer()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import jdk.internal.classfile.impl.AccessFlagsImpl;
import jdk.internal.classfile.impl.ChainedFieldBuilder;
import jdk.internal.classfile.impl.TerminalFieldBuilder;
import jdk.internal.classfile.impl.TransformImpl;

/**
* A builder for fields. The main way to obtain a field builder is via {@link
Expand All @@ -48,7 +49,7 @@
* @since 24
*/
public sealed interface FieldBuilder
extends ClassFileBuilder<FieldElement, FieldBuilder>
extends ClassFileBuilder<FieldTransform, FieldElement, FieldBuilder>
permits TerminalFieldBuilder, ChainedFieldBuilder {

/**
Expand Down Expand Up @@ -77,4 +78,17 @@ default FieldBuilder withFlags(AccessFlag... flags) {
return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags));
}

// Implementations

/**
* @since 25
*/
@Override
default FieldBuilder transforming(FieldTransform transform, Consumer<? super FieldBuilder> handler) {
var resolved = TransformImpl.resolve(transform, this);
resolved.startHandler().run();
handler.accept(new ChainedFieldBuilder(this, resolved.consumer()));
resolved.endHandler().run();
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import jdk.internal.classfile.impl.AccessFlagsImpl;
import jdk.internal.classfile.impl.ChainedMethodBuilder;
import jdk.internal.classfile.impl.TerminalMethodBuilder;
import jdk.internal.classfile.impl.TransformImpl;

/**
* A builder for methods. The main way to obtain a method builder is via {@link
Expand All @@ -49,7 +50,7 @@
* @since 24
*/
public sealed interface MethodBuilder
extends ClassFileBuilder<MethodElement, MethodBuilder>
extends ClassFileBuilder<MethodTransform, MethodElement, MethodBuilder>
permits ChainedMethodBuilder, TerminalMethodBuilder {

/**
Expand Down Expand Up @@ -106,4 +107,18 @@ default MethodBuilder withFlags(AccessFlag... flags) {
* @see CodeTransform
*/
MethodBuilder transformCode(CodeModel code, CodeTransform transform);

// Implementations

/**
* @since 25
*/
@Override
default MethodBuilder transforming(MethodTransform transform, Consumer<? super MethodBuilder> handler) {
var resolved = TransformImpl.resolve(transform, this);
resolved.startHandler().run();
handler.accept(new ChainedMethodBuilder(this, resolved.consumer()));
resolved.endHandler().run();
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ private static String makeDumpableClassName(String className) {

record ClassData(FieldRefEntry field, Object value) {}

FieldRefEntry classData(ClassFileBuilder<?, ?> cfb, Object arg, ClassDesc desc) {
FieldRefEntry classData(ClassFileBuilder<?, ?, ?> cfb, Object arg, ClassDesc desc) {
// unique static variable name
String name;
List<ClassData> classData = this.classData;
Expand Down
Loading