8345486: Reevaluate the classes in java.lang.classfile.components package

Reviewed-by: mcimadamore, asotona
This commit is contained in:
Chen Liang 2024-12-04 17:30:01 +00:00
parent f3b4350e0f
commit 79eb77b782
33 changed files with 54 additions and 158 deletions

View file

@ -60,12 +60,7 @@ import java.util.function.Supplier;
* its state must be reset for each traversal; this will happen automatically if
* the transform is created with {@link ClassTransform#ofStateful(Supplier)} (or
* corresponding methods for other classfile locations.)
* <p>
* Class transformation sample where code transformation is stateful:
* {@snippet lang="java" class="PackageSnippets" region="codeRelabeling"}
* <p>
* Complex class instrumentation sample chaining multiple transformations:
* {@snippet lang="java" class="PackageSnippets" region="classInstrumentation"}
*
* @param <C> the transform type
* @param <E> the element type
* @param <B> the builder type

View file

@ -25,21 +25,13 @@
package java.lang.classfile.snippets;
import java.lang.classfile.*;
import java.lang.classfile.components.ClassRemapper;
import java.lang.classfile.components.CodeLocalsShifter;
import java.lang.classfile.components.CodeRelabeler;
import java.lang.classfile.instruction.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessFlag;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
@ -326,82 +318,6 @@ class PackageSnippets {
// @end
}
void codeRelabeling(ClassModel classModel) {
// @start region="codeRelabeling"
byte[] newBytes = ClassFile.of().transformClass(classModel,
ClassTransform.transformingMethodBodies(
CodeTransform.ofStateful(CodeRelabeler::of)));
// @end
}
// @start region="classInstrumentation"
byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate<MethodModel> instrumentedMethodsFilter) {
var instrumentorCodeMap = instrumentor.methods().stream()
.filter(instrumentedMethodsFilter)
.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 ClassFile.of().transformClass(target,
ClassTransform.transformingMethods(
instrumentedMethodsFilter,
(mb, me) -> {
if (me instanceof CodeModel targetCodeModel) {
var mm = targetCodeModel.parent().get();
//instrumented methods code is taken from instrumentor
mb.transformCode(instrumentorCodeMap.get(mm.methodName().stringValue() + mm.methodType().stringValue()),
//all references to the instrumentor class are remapped to target class
instrumentorClassRemapper.asCodeTransform()
.andThen((codeBuilder, instrumentorCodeElement) -> {
//all invocations of target methods from instrumentor are inlined
if (instrumentorCodeElement instanceof InvokeInstruction inv
&& target.thisClass().asInternalName().equals(inv.owner().asInternalName())
&& mm.methodName().stringValue().equals(inv.name().stringValue())
&& mm.methodType().stringValue().equals(inv.type().stringValue())) {
//store stacked method parameters into locals
var storeStack = new ArrayDeque<StoreInstruction>();
int slot = 0;
if (!mm.flags().has(AccessFlag.STATIC))
storeStack.push(StoreInstruction.of(TypeKind.REFERENCE, slot++));
for (var pt : mm.methodTypeSymbol().parameterList()) {
var tk = TypeKind.from(pt);
storeStack.push(StoreInstruction.of(tk, slot));
slot += tk.slotSize();
}
storeStack.forEach(codeBuilder::with);
//inlined target locals must be shifted based on the actual instrumentor locals
codeBuilder.block(inlinedBlockBuilder -> inlinedBlockBuilder
.transform(targetCodeModel, CodeLocalsShifter.of(mm.flags(), mm.methodTypeSymbol())
.andThen(CodeRelabeler.of())
.andThen((innerBuilder, shiftedTargetCode) -> {
//returns must be replaced with jump to the end of the inlined method
if (shiftedTargetCode instanceof ReturnInstruction)
innerBuilder.goto_(inlinedBlockBuilder.breakLabel());
else
innerBuilder.with(shiftedTargetCode);
})));
} else
codeBuilder.with(instrumentorCodeElement);
}));
} else
mb.with(me);
})
.andThen(ClassTransform.endHandler(clb ->
//remaining instrumentor fields and methods are injected at the end
clb.transform(instrumentor,
ClassTransform.dropping(cle ->
!(cle instanceof FieldModel fm
&& !targetFieldNames.contains(fm.fieldName().stringValue()))
&& !(cle instanceof MethodModel mm
&& !ConstantDescs.INIT_NAME.equals(mm.methodName().stringValue())
&& !targetMethods.contains(mm.methodName().stringValue() + mm.methodType().stringValue())))
//and instrumentor class references remapped to target class
.andThen(instrumentorClassRemapper)))));
}
// @end
void resolverExample() {
// @start region="lookup-class-hierarchy-resolver"
MethodHandles.Lookup lookup = MethodHandles.lookup(); // @replace regex="MethodHandles\.lookup\(\)" replacement="..."

View file

@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;
import java.lang.classfile.ClassModel;
import java.lang.classfile.CodeModel;
@ -58,8 +58,6 @@ import jdk.internal.classfile.impl.ClassPrinterImpl;
* <p>
* Another use case for {@link ClassPrinter} is to simplify writing of automated tests:
* {@snippet lang="java" class="PackageSnippets" region="printNodesInTest"}
*
* @since 24
*/
public final class ClassPrinter {
@ -68,8 +66,6 @@ public final class ClassPrinter {
/**
* Level of detail to print or export.
*
* @since 24
*/
public enum Verbosity {
@ -102,8 +98,6 @@ public final class ClassPrinter {
/**
* Named, traversable, and printable node parent.
*
* @since 24
*/
public sealed interface Node {
@ -146,8 +140,6 @@ public final class ClassPrinter {
/**
* A leaf node holding single printable value.
*
* @since 24
*/
public sealed interface LeafNode extends Node
permits ClassPrinterImpl.LeafNodeImpl {
@ -161,8 +153,6 @@ public final class ClassPrinter {
/**
* A tree node holding {@link List} of nested nodes.
*
* @since 24
*/
public sealed interface ListNode extends Node, List<Node>
permits ClassPrinterImpl.ListNodeImpl {
@ -172,8 +162,6 @@ public final class ClassPrinter {
* A tree node holding {@link Map} of nested nodes.
* <p>
* Each {@link Map.Entry#getKey()} == {@link Map.Entry#getValue()}.{@link #name()}.
*
* @since 24
*/
public sealed interface MapNode extends Node, Map<ConstantDesc, Node>
permits ClassPrinterImpl.MapNodeImpl {

View file

@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
@ -53,8 +53,6 @@ import static java.util.Objects.requireNonNull;
* <p>
* Arrays of reference types are always decomposed, mapped as the base reference
* types and composed back to arrays.
*
* @since 24
*/
public sealed interface ClassRemapper extends ClassTransform permits ClassRemapperImpl {

View file

@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;
import java.lang.classfile.AccessFlags;
import java.lang.classfile.CodeTransform;
@ -37,8 +37,6 @@ import jdk.internal.classfile.impl.CodeLocalsShifterImpl;
* newly allocated positions to avoid conflicts during code injection.
* Locals pointing to the receiver or to method arguments slots are never shifted.
* All locals pointing beyond the method arguments are re-indexed in order of appearance.
*
* @since 24
*/
public sealed interface CodeLocalsShifter extends CodeTransform permits CodeLocalsShifterImpl {

View file

@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeTransform;
@ -44,8 +44,6 @@ import static java.util.Objects.requireNonNull;
* Primary purpose of CodeRelabeler is for repeated injections of the same code blocks.
* Repeated injection of the same code block must be relabeled, so each instance of
* {@link java.lang.classfile.Label} is bound in the target bytecode exactly once.
*
* @since 24
*/
public sealed interface CodeRelabeler extends CodeTransform permits CodeRelabelerImpl {

View file

@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;
import java.lang.classfile.CodeTransform;
import java.lang.classfile.Label;
@ -49,8 +49,6 @@ import jdk.internal.classfile.impl.CodeStackTrackerImpl;
* int maxStack = stackTracker.maxStackSize().get();
* });
* }
*
* @since 24
*/
public sealed interface CodeStackTracker extends CodeTransform permits CodeStackTrackerImpl {

View file

@ -110,8 +110,6 @@
* CodeLocalsShifter} and {@link CodeRelabeler} into fully functional class
* instrumenting transformation:
* {@snippet lang="java" class="PackageSnippets" region="classInstrumentation"}
*
* @since 24
*/
package java.lang.classfile.components;
package jdk.internal.classfile.components;

View file

@ -25,10 +25,10 @@
package java.lang.classfile.components.snippets;
import java.lang.classfile.*;
import java.lang.classfile.components.ClassPrinter;
import java.lang.classfile.components.ClassRemapper;
import java.lang.classfile.components.CodeLocalsShifter;
import java.lang.classfile.components.CodeRelabeler;
import jdk.internal.classfile.components.ClassPrinter;
import jdk.internal.classfile.components.ClassRemapper;
import jdk.internal.classfile.components.CodeLocalsShifter;
import jdk.internal.classfile.components.CodeRelabeler;
import java.lang.classfile.instruction.InvokeInstruction;
import java.lang.classfile.instruction.ReturnInstruction;
import java.lang.classfile.instruction.StoreInstruction;

View file

@ -31,11 +31,11 @@ import java.lang.classfile.attribute.StackMapFrameInfo.ObjectVerificationTypeInf
import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo;
import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo;
import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo;
import java.lang.classfile.components.ClassPrinter.LeafNode;
import java.lang.classfile.components.ClassPrinter.ListNode;
import java.lang.classfile.components.ClassPrinter.MapNode;
import java.lang.classfile.components.ClassPrinter.Node;
import java.lang.classfile.components.ClassPrinter.Verbosity;
import jdk.internal.classfile.components.ClassPrinter.LeafNode;
import jdk.internal.classfile.components.ClassPrinter.ListNode;
import jdk.internal.classfile.components.ClassPrinter.MapNode;
import jdk.internal.classfile.components.ClassPrinter.Node;
import jdk.internal.classfile.components.ClassPrinter.Verbosity;
import java.lang.classfile.constantpool.*;
import java.lang.classfile.instruction.*;
import java.lang.constant.ConstantDesc;

View file

@ -26,7 +26,7 @@ package jdk.internal.classfile.impl;
import java.lang.classfile.*;
import java.lang.classfile.attribute.*;
import java.lang.classfile.components.ClassRemapper;
import jdk.internal.classfile.components.ClassRemapper;
import java.lang.classfile.constantpool.Utf8Entry;
import java.lang.classfile.instruction.ConstantInstruction.LoadConstantInstruction;
import java.lang.classfile.instruction.*;

View file

@ -28,7 +28,7 @@ import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.Signature;
import java.lang.classfile.TypeKind;
import java.lang.classfile.components.CodeLocalsShifter;
import jdk.internal.classfile.components.CodeLocalsShifter;
import java.lang.classfile.instruction.IncrementInstruction;
import java.lang.classfile.instruction.LoadInstruction;
import java.lang.classfile.instruction.LocalVariable;

View file

@ -27,7 +27,7 @@ package jdk.internal.classfile.impl;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.Label;
import java.lang.classfile.components.CodeRelabeler;
import jdk.internal.classfile.components.CodeRelabeler;
import java.lang.classfile.instruction.*;
import java.util.function.BiFunction;

View file

@ -29,7 +29,7 @@ import java.lang.classfile.CodeElement;
import java.lang.classfile.Label;
import java.lang.classfile.Opcode;
import java.lang.classfile.TypeKind;
import java.lang.classfile.components.CodeStackTracker;
import jdk.internal.classfile.components.CodeStackTracker;
import java.lang.classfile.instruction.*;
import java.util.*;
import java.util.function.Consumer;

View file

@ -26,7 +26,7 @@ package jdk.internal.classfile.impl;
import java.lang.classfile.*;
import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.components.ClassPrinter;
import jdk.internal.classfile.components.ClassPrinter;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ModuleEntry;
import java.lang.classfile.constantpool.PoolEntry;

View file

@ -26,7 +26,7 @@ package jdk.internal.classfile.impl.verifier;
import java.lang.classfile.ClassHierarchyResolver;
import java.lang.classfile.ClassModel;
import java.lang.classfile.components.ClassPrinter;
import jdk.internal.classfile.components.ClassPrinter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

View file

@ -81,7 +81,6 @@ module java.base {
exports java.lang.annotation;
exports java.lang.classfile;
exports java.lang.classfile.attribute;
exports java.lang.classfile.components;
exports java.lang.classfile.constantpool;
exports java.lang.classfile.instruction;
exports java.lang.constant;
@ -177,6 +176,8 @@ module java.base {
jdk.net,
jdk.sctp,
jdk.crypto.cryptoki;
exports jdk.internal.classfile.components to
jdk.jfr;
exports jdk.internal.foreign to
jdk.incubator.vector;
exports jdk.internal.event to