8306457: Classfile API components implementations should not be exposed
Reviewed-by: asotona
This commit is contained in:
parent
f4f5542f8d
commit
3c9ec26370
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -19,79 +19,19 @@
|
|||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 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
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
* questions.
|
* questions.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package jdk.internal.classfile.components;
|
package jdk.internal.classfile.components;
|
||||||
|
|
||||||
import java.lang.constant.ClassDesc;
|
import java.lang.constant.ClassDesc;
|
||||||
import java.lang.constant.ConstantDesc;
|
|
||||||
import java.lang.constant.DirectMethodHandleDesc;
|
|
||||||
import java.lang.constant.DynamicCallSiteDesc;
|
|
||||||
import java.lang.constant.DynamicConstantDesc;
|
|
||||||
import java.lang.constant.MethodHandleDesc;
|
|
||||||
import java.lang.constant.MethodTypeDesc;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import jdk.internal.classfile.Annotation;
|
|
||||||
import jdk.internal.classfile.AnnotationElement;
|
|
||||||
import jdk.internal.classfile.AnnotationValue;
|
|
||||||
import jdk.internal.classfile.ClassBuilder;
|
|
||||||
import jdk.internal.classfile.ClassElement;
|
|
||||||
import jdk.internal.classfile.ClassModel;
|
import jdk.internal.classfile.ClassModel;
|
||||||
import jdk.internal.classfile.ClassSignature;
|
|
||||||
import jdk.internal.classfile.ClassTransform;
|
import jdk.internal.classfile.ClassTransform;
|
||||||
import jdk.internal.classfile.Classfile;
|
import jdk.internal.classfile.Classfile;
|
||||||
import jdk.internal.classfile.CodeBuilder;
|
|
||||||
import jdk.internal.classfile.CodeElement;
|
|
||||||
import jdk.internal.classfile.CodeModel;
|
|
||||||
import jdk.internal.classfile.CodeTransform;
|
import jdk.internal.classfile.CodeTransform;
|
||||||
import jdk.internal.classfile.FieldBuilder;
|
|
||||||
import jdk.internal.classfile.FieldElement;
|
|
||||||
import jdk.internal.classfile.FieldModel;
|
|
||||||
import jdk.internal.classfile.FieldTransform;
|
import jdk.internal.classfile.FieldTransform;
|
||||||
import jdk.internal.classfile.Interfaces;
|
|
||||||
import jdk.internal.classfile.MethodBuilder;
|
|
||||||
import jdk.internal.classfile.MethodElement;
|
|
||||||
import jdk.internal.classfile.impl.TemporaryConstantPool;
|
|
||||||
import jdk.internal.classfile.instruction.FieldInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.InvokeDynamicInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.InvokeInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.NewMultiArrayInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.NewObjectInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.NewReferenceArrayInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.TypeCheckInstruction;
|
|
||||||
import jdk.internal.classfile.MethodModel;
|
|
||||||
import jdk.internal.classfile.MethodSignature;
|
|
||||||
import jdk.internal.classfile.MethodTransform;
|
import jdk.internal.classfile.MethodTransform;
|
||||||
import jdk.internal.classfile.Signature;
|
import jdk.internal.classfile.impl.ClassRemapperImpl;
|
||||||
import jdk.internal.classfile.Superclass;
|
|
||||||
import jdk.internal.classfile.TypeAnnotation;
|
|
||||||
import jdk.internal.classfile.attribute.AnnotationDefaultAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.EnclosingMethodAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.ExceptionsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.InnerClassInfo;
|
|
||||||
import jdk.internal.classfile.attribute.InnerClassesAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.ModuleAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.ModuleProvideInfo;
|
|
||||||
import jdk.internal.classfile.attribute.NestHostAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.NestMembersAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.PermittedSubclassesAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RecordAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RecordComponentInfo;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
|
|
||||||
import jdk.internal.classfile.attribute.SignatureAttribute;
|
|
||||||
import jdk.internal.classfile.constantpool.Utf8Entry;
|
|
||||||
import jdk.internal.classfile.instruction.ExceptionCatch;
|
|
||||||
import jdk.internal.classfile.instruction.LocalVariable;
|
|
||||||
import jdk.internal.classfile.instruction.LocalVariableType;
|
|
||||||
import jdk.internal.classfile.impl.Util;
|
|
||||||
import jdk.internal.classfile.instruction.ConstantInstruction.LoadConstantInstruction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code ClassRemapper} is a {@link ClassTransform}, {@link FieldTransform},
|
* {@code ClassRemapper} is a {@link ClassTransform}, {@link FieldTransform},
|
||||||
@ -109,7 +49,7 @@ import jdk.internal.classfile.instruction.ConstantInstruction.LoadConstantInstru
|
|||||||
* Arrays of reference types are always decomposed, mapped as the base reference
|
* Arrays of reference types are always decomposed, mapped as the base reference
|
||||||
* types and composed back to arrays.
|
* types and composed back to arrays.
|
||||||
*/
|
*/
|
||||||
public sealed interface ClassRemapper extends ClassTransform {
|
public sealed interface ClassRemapper extends ClassTransform permits ClassRemapperImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of {@code ClassRemapper} instructed with a class map.
|
* Creates new instance of {@code ClassRemapper} instructed with a class map.
|
||||||
@ -135,25 +75,22 @@ public sealed interface ClassRemapper extends ClassTransform {
|
|||||||
/**
|
/**
|
||||||
* Access method to internal class mapping function.
|
* Access method to internal class mapping function.
|
||||||
* @param desc source class
|
* @param desc source class
|
||||||
* @return class target class
|
* @return target class
|
||||||
*/
|
*/
|
||||||
ClassDesc map(ClassDesc desc);
|
ClassDesc map(ClassDesc desc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code ClassRemapper} as {@link FieldTransform} instance
|
* {@return this {@code ClassRemapper} as {@link FieldTransform} instance}
|
||||||
* @return this {@code ClassRemapper} as {@link FieldTransform} instance
|
|
||||||
*/
|
*/
|
||||||
FieldTransform asFieldTransform();
|
FieldTransform asFieldTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code ClassRemapper} as {@link MethodTransform} instance
|
* {@return this {@code ClassRemapper} as {@link MethodTransform} instance}
|
||||||
* @return this {@code ClassRemapper} as {@link MethodTransform} instance
|
|
||||||
*/
|
*/
|
||||||
MethodTransform asMethodTransform();
|
MethodTransform asMethodTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code ClassRemapper} as {@link CodeTransform} instance
|
* {@return this {@code ClassRemapper} as {@link CodeTransform} instance}
|
||||||
* @return this {@code ClassRemapper} as {@link CodeTransform} instance
|
|
||||||
*/
|
*/
|
||||||
CodeTransform asCodeTransform();
|
CodeTransform asCodeTransform();
|
||||||
|
|
||||||
@ -166,330 +103,4 @@ public sealed interface ClassRemapper extends ClassTransform {
|
|||||||
return Classfile.build(map(clm.thisClass().asSymbol()),
|
return Classfile.build(map(clm.thisClass().asSymbol()),
|
||||||
clb -> clm.forEachElement(resolve(clb).consumer()));
|
clb -> clm.forEachElement(resolve(clb).consumer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
record ClassRemapperImpl(Function<ClassDesc, ClassDesc> mapFunction) implements ClassRemapper {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(ClassBuilder clb, ClassElement cle) {
|
|
||||||
switch (cle) {
|
|
||||||
case FieldModel fm ->
|
|
||||||
clb.withField(fm.fieldName().stringValue(), map(
|
|
||||||
fm.fieldTypeSymbol()), fb ->
|
|
||||||
fm.forEachElement(asFieldTransform().resolve(fb).consumer()));
|
|
||||||
case MethodModel mm ->
|
|
||||||
clb.withMethod(mm.methodName().stringValue(), mapMethodDesc(
|
|
||||||
mm.methodTypeSymbol()), mm.flags().flagsMask(), mb ->
|
|
||||||
mm.forEachElement(asMethodTransform().resolve(mb).consumer()));
|
|
||||||
case Superclass sc ->
|
|
||||||
clb.withSuperclass(map(sc.superclassEntry().asSymbol()));
|
|
||||||
case Interfaces ins ->
|
|
||||||
clb.withInterfaceSymbols(Util.mappedList(ins.interfaces(), in ->
|
|
||||||
map(in.asSymbol())));
|
|
||||||
case SignatureAttribute sa ->
|
|
||||||
clb.with(SignatureAttribute.of(mapClassSignature(sa.asClassSignature())));
|
|
||||||
case InnerClassesAttribute ica ->
|
|
||||||
clb.with(InnerClassesAttribute.of(ica.classes().stream().map(ici ->
|
|
||||||
InnerClassInfo.of(map(ici.innerClass().asSymbol()),
|
|
||||||
ici.outerClass().map(oc -> map(oc.asSymbol())),
|
|
||||||
ici.innerName().map(Utf8Entry::stringValue),
|
|
||||||
ici.flagsMask())).toList()));
|
|
||||||
case EnclosingMethodAttribute ema ->
|
|
||||||
clb.with(EnclosingMethodAttribute.of(map(ema.enclosingClass().asSymbol()),
|
|
||||||
ema.enclosingMethodName().map(Utf8Entry::stringValue),
|
|
||||||
ema.enclosingMethodTypeSymbol().map(this::mapMethodDesc)));
|
|
||||||
case RecordAttribute ra ->
|
|
||||||
clb.with(RecordAttribute.of(ra.components().stream()
|
|
||||||
.map(this::mapRecordComponent).toList()));
|
|
||||||
case ModuleAttribute ma ->
|
|
||||||
clb.with(ModuleAttribute.of(ma.moduleName(), ma.moduleFlagsMask(),
|
|
||||||
ma.moduleVersion().orElse(null),
|
|
||||||
ma.requires(), ma.exports(), ma.opens(),
|
|
||||||
ma.uses().stream().map(ce ->
|
|
||||||
clb.constantPool().classEntry(map(ce.asSymbol()))).toList(),
|
|
||||||
ma.provides().stream().map(mp ->
|
|
||||||
ModuleProvideInfo.of(map(mp.provides().asSymbol()),
|
|
||||||
mp.providesWith().stream().map(pw ->
|
|
||||||
map(pw.asSymbol())).toList())).toList()));
|
|
||||||
case NestHostAttribute nha ->
|
|
||||||
clb.with(NestHostAttribute.of(map(nha.nestHost().asSymbol())));
|
|
||||||
case NestMembersAttribute nma ->
|
|
||||||
clb.with(NestMembersAttribute.ofSymbols(nma.nestMembers().stream()
|
|
||||||
.map(nm -> map(nm.asSymbol())).toList()));
|
|
||||||
case PermittedSubclassesAttribute psa ->
|
|
||||||
clb.with(PermittedSubclassesAttribute.ofSymbols(
|
|
||||||
psa.permittedSubclasses().stream().map(ps ->
|
|
||||||
map(ps.asSymbol())).toList()));
|
|
||||||
case RuntimeVisibleAnnotationsAttribute aa ->
|
|
||||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleAnnotationsAttribute aa ->
|
|
||||||
clb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
|
||||||
clb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
|
||||||
clb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
default ->
|
|
||||||
clb.with(cle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FieldTransform asFieldTransform() {
|
|
||||||
return (FieldBuilder fb, FieldElement fe) -> {
|
|
||||||
switch (fe) {
|
|
||||||
case SignatureAttribute sa ->
|
|
||||||
fb.with(SignatureAttribute.of(
|
|
||||||
mapSignature(sa.asTypeSignature())));
|
|
||||||
case RuntimeVisibleAnnotationsAttribute aa ->
|
|
||||||
fb.with(RuntimeVisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleAnnotationsAttribute aa ->
|
|
||||||
fb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
|
||||||
fb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
|
||||||
fb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
default ->
|
|
||||||
fb.with(fe);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodTransform asMethodTransform() {
|
|
||||||
return (MethodBuilder mb, MethodElement me) -> {
|
|
||||||
switch (me) {
|
|
||||||
case AnnotationDefaultAttribute ada ->
|
|
||||||
mb.with(AnnotationDefaultAttribute.of(
|
|
||||||
mapAnnotationValue(ada.defaultValue())));
|
|
||||||
case CodeModel com ->
|
|
||||||
mb.transformCode(com, asCodeTransform());
|
|
||||||
case ExceptionsAttribute ea ->
|
|
||||||
mb.with(ExceptionsAttribute.ofSymbols(
|
|
||||||
ea.exceptions().stream().map(ce ->
|
|
||||||
map(ce.asSymbol())).toList()));
|
|
||||||
case SignatureAttribute sa ->
|
|
||||||
mb.with(SignatureAttribute.of(
|
|
||||||
mapMethodSignature(sa.asMethodSignature())));
|
|
||||||
case RuntimeVisibleAnnotationsAttribute aa ->
|
|
||||||
mb.with(RuntimeVisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleAnnotationsAttribute aa ->
|
|
||||||
mb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations())));
|
|
||||||
case RuntimeVisibleParameterAnnotationsAttribute paa ->
|
|
||||||
mb.with(RuntimeVisibleParameterAnnotationsAttribute.of(
|
|
||||||
paa.parameterAnnotations().stream()
|
|
||||||
.map(this::mapAnnotations).toList()));
|
|
||||||
case RuntimeInvisibleParameterAnnotationsAttribute paa ->
|
|
||||||
mb.with(RuntimeInvisibleParameterAnnotationsAttribute.of(
|
|
||||||
paa.parameterAnnotations().stream()
|
|
||||||
.map(this::mapAnnotations).toList()));
|
|
||||||
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
|
||||||
mb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
|
||||||
mb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
default ->
|
|
||||||
mb.with(me);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CodeTransform asCodeTransform() {
|
|
||||||
return (CodeBuilder cob, CodeElement coe) -> {
|
|
||||||
switch (coe) {
|
|
||||||
case FieldInstruction fai ->
|
|
||||||
cob.fieldInstruction(fai.opcode(), map(fai.owner().asSymbol()),
|
|
||||||
fai.name().stringValue(), map(fai.typeSymbol()));
|
|
||||||
case InvokeInstruction ii ->
|
|
||||||
cob.invokeInstruction(ii.opcode(), map(ii.owner().asSymbol()),
|
|
||||||
ii.name().stringValue(), mapMethodDesc(ii.typeSymbol()),
|
|
||||||
ii.isInterface());
|
|
||||||
case InvokeDynamicInstruction idi ->
|
|
||||||
cob.invokeDynamicInstruction(DynamicCallSiteDesc.of(
|
|
||||||
idi.bootstrapMethod(), idi.name().stringValue(),
|
|
||||||
mapMethodDesc(idi.typeSymbol()),
|
|
||||||
idi.bootstrapArgs().stream().map(this::mapConstantValue).toArray(ConstantDesc[]::new)));
|
|
||||||
case NewObjectInstruction c ->
|
|
||||||
cob.newObjectInstruction(map(c.className().asSymbol()));
|
|
||||||
case NewReferenceArrayInstruction c ->
|
|
||||||
cob.anewarray(map(c.componentType().asSymbol()));
|
|
||||||
case NewMultiArrayInstruction c ->
|
|
||||||
cob.multianewarray(map(c.arrayType().asSymbol()), c.dimensions());
|
|
||||||
case TypeCheckInstruction c ->
|
|
||||||
cob.typeCheckInstruction(c.opcode(), map(c.type().asSymbol()));
|
|
||||||
case ExceptionCatch c ->
|
|
||||||
cob.exceptionCatch(c.tryStart(), c.tryEnd(), c.handler(),c.catchType()
|
|
||||||
.map(d -> TemporaryConstantPool.INSTANCE.classEntry(map(d.asSymbol()))));
|
|
||||||
case LocalVariable c ->
|
|
||||||
cob.localVariable(c.slot(), c.name().stringValue(), map(c.typeSymbol()),
|
|
||||||
c.startScope(), c.endScope());
|
|
||||||
case LocalVariableType c ->
|
|
||||||
cob.localVariableType(c.slot(), c.name().stringValue(),
|
|
||||||
mapSignature(c.signatureSymbol()), c.startScope(), c.endScope());
|
|
||||||
case LoadConstantInstruction ldc ->
|
|
||||||
cob.constantInstruction(ldc.opcode(),
|
|
||||||
mapConstantValue(ldc.constantValue()));
|
|
||||||
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
|
||||||
cob.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
|
||||||
cob.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations())));
|
|
||||||
default ->
|
|
||||||
cob.with(coe);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassDesc map(ClassDesc desc) {
|
|
||||||
if (desc == null) return null;
|
|
||||||
if (desc.isArray()) return map(desc.componentType()).arrayType();
|
|
||||||
if (desc.isPrimitive()) return desc;
|
|
||||||
return mapFunction.apply(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodTypeDesc mapMethodDesc(MethodTypeDesc desc) {
|
|
||||||
return MethodTypeDesc.of(map(desc.returnType()),
|
|
||||||
desc.parameterList().stream().map(this::map).toArray(ClassDesc[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassSignature mapClassSignature(ClassSignature signature) {
|
|
||||||
return ClassSignature.of(mapTypeParams(signature.typeParameters()),
|
|
||||||
mapSignature(signature.superclassSignature()),
|
|
||||||
signature.superinterfaceSignatures().stream()
|
|
||||||
.map(this::mapSignature).toArray(Signature.RefTypeSig[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodSignature mapMethodSignature(MethodSignature signature) {
|
|
||||||
return MethodSignature.of(mapTypeParams(signature.typeParameters()),
|
|
||||||
signature.throwableSignatures().stream().map(this::mapSignature).toList(),
|
|
||||||
mapSignature(signature.result()),
|
|
||||||
signature.arguments().stream()
|
|
||||||
.map(this::mapSignature).toArray(Signature[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordComponentInfo mapRecordComponent(RecordComponentInfo component) {
|
|
||||||
return RecordComponentInfo.of(component.name().stringValue(),
|
|
||||||
map(component.descriptorSymbol()),
|
|
||||||
component.attributes().stream().map(atr ->
|
|
||||||
switch (atr) {
|
|
||||||
case SignatureAttribute sa ->
|
|
||||||
SignatureAttribute.of(
|
|
||||||
mapSignature(sa.asTypeSignature()));
|
|
||||||
case RuntimeVisibleAnnotationsAttribute aa ->
|
|
||||||
RuntimeVisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations()));
|
|
||||||
case RuntimeInvisibleAnnotationsAttribute aa ->
|
|
||||||
RuntimeInvisibleAnnotationsAttribute.of(
|
|
||||||
mapAnnotations(aa.annotations()));
|
|
||||||
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
|
||||||
RuntimeVisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations()));
|
|
||||||
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
|
||||||
RuntimeInvisibleTypeAnnotationsAttribute.of(
|
|
||||||
mapTypeAnnotations(aa.annotations()));
|
|
||||||
default -> atr;
|
|
||||||
}).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectMethodHandleDesc mapDirectMethodHandle(DirectMethodHandleDesc dmhd) {
|
|
||||||
return switch (dmhd.kind()) {
|
|
||||||
case GETTER, SETTER, STATIC_GETTER, STATIC_SETTER ->
|
|
||||||
MethodHandleDesc.ofField(dmhd.kind(), map(dmhd.owner()),
|
|
||||||
dmhd.methodName(),
|
|
||||||
map(ClassDesc.ofDescriptor(dmhd.lookupDescriptor())));
|
|
||||||
default ->
|
|
||||||
MethodHandleDesc.ofMethod(dmhd.kind(), map(dmhd.owner()),
|
|
||||||
dmhd.methodName(),
|
|
||||||
mapMethodDesc(MethodTypeDesc.ofDescriptor(dmhd.lookupDescriptor())));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstantDesc mapConstantValue(ConstantDesc value) {
|
|
||||||
return switch (value) {
|
|
||||||
case ClassDesc cd ->
|
|
||||||
map(cd);
|
|
||||||
case DynamicConstantDesc<?> dcd ->
|
|
||||||
mapDynamicConstant(dcd);
|
|
||||||
case DirectMethodHandleDesc dmhd ->
|
|
||||||
mapDirectMethodHandle(dmhd);
|
|
||||||
case MethodTypeDesc mtd ->
|
|
||||||
mapMethodDesc(mtd);
|
|
||||||
default -> value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicConstantDesc<?> mapDynamicConstant(DynamicConstantDesc<?> dcd) {
|
|
||||||
return DynamicConstantDesc.ofNamed(mapDirectMethodHandle(dcd.bootstrapMethod()),
|
|
||||||
dcd.constantName(),
|
|
||||||
map(dcd.constantType()),
|
|
||||||
dcd.bootstrapArgsList().stream().map(this::mapConstantValue).toArray(ConstantDesc[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
<S extends Signature> S mapSignature(S signature) {
|
|
||||||
return (S) switch (signature) {
|
|
||||||
case Signature.ArrayTypeSig ats ->
|
|
||||||
Signature.ArrayTypeSig.of(mapSignature(ats.componentSignature()));
|
|
||||||
case Signature.ClassTypeSig cts ->
|
|
||||||
Signature.ClassTypeSig.of(
|
|
||||||
cts.outerType().map(this::mapSignature).orElse(null),
|
|
||||||
map(cts.classDesc()),
|
|
||||||
cts.typeArgs().stream()
|
|
||||||
.map(ta -> Signature.TypeArg.of(
|
|
||||||
ta.wildcardIndicator(),
|
|
||||||
ta.boundType().map(this::mapSignature)))
|
|
||||||
.toArray(Signature.TypeArg[]::new));
|
|
||||||
default -> signature;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Annotation> mapAnnotations(List<Annotation> annotations) {
|
|
||||||
return annotations.stream().map(this::mapAnnotation).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Annotation mapAnnotation(Annotation a) {
|
|
||||||
return Annotation.of(map(a.classSymbol()), a.elements().stream().map(el ->
|
|
||||||
AnnotationElement.of(el.name(), mapAnnotationValue(el.value()))).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
AnnotationValue mapAnnotationValue(AnnotationValue val) {
|
|
||||||
return switch (val) {
|
|
||||||
case AnnotationValue.OfAnnotation oa ->
|
|
||||||
AnnotationValue.ofAnnotation(mapAnnotation(oa.annotation()));
|
|
||||||
case AnnotationValue.OfArray oa ->
|
|
||||||
AnnotationValue.ofArray(oa.values().stream().map(this::mapAnnotationValue).toList());
|
|
||||||
case AnnotationValue.OfConstant oc -> oc;
|
|
||||||
case AnnotationValue.OfClass oc ->
|
|
||||||
AnnotationValue.ofClass(map(oc.classSymbol()));
|
|
||||||
case AnnotationValue.OfEnum oe ->
|
|
||||||
AnnotationValue.ofEnum(map(oe.classSymbol()), oe.constantName().stringValue());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TypeAnnotation> mapTypeAnnotations(List<TypeAnnotation> typeAnnotations) {
|
|
||||||
return typeAnnotations.stream().map(a -> TypeAnnotation.of(a.targetInfo(),
|
|
||||||
a.targetPath(), map(a.classSymbol()),
|
|
||||||
a.elements().stream().map(el -> AnnotationElement.of(el.name(),
|
|
||||||
mapAnnotationValue(el.value()))).toList())).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Signature.TypeParam> mapTypeParams(List<Signature.TypeParam> typeParams) {
|
|
||||||
return typeParams.stream().map(tp -> Signature.TypeParam.of(tp.identifier(),
|
|
||||||
tp.classBound().map(this::mapSignature),
|
|
||||||
tp.interfaceBounds().stream()
|
|
||||||
.map(this::mapSignature).toArray(Signature.RefTypeSig[]::new))).toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -19,25 +19,15 @@
|
|||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 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
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
* questions.
|
* questions.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package jdk.internal.classfile.components;
|
package jdk.internal.classfile.components;
|
||||||
|
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import java.lang.reflect.AccessFlag;
|
import java.lang.reflect.AccessFlag;
|
||||||
import jdk.internal.classfile.AccessFlags;
|
import jdk.internal.classfile.AccessFlags;
|
||||||
import jdk.internal.classfile.CodeBuilder;
|
|
||||||
import jdk.internal.classfile.CodeElement;
|
|
||||||
import jdk.internal.classfile.CodeTransform;
|
import jdk.internal.classfile.CodeTransform;
|
||||||
import jdk.internal.classfile.Signature;
|
|
||||||
import jdk.internal.classfile.instruction.IncrementInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.LoadInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.LocalVariable;
|
|
||||||
import jdk.internal.classfile.instruction.StoreInstruction;
|
|
||||||
import jdk.internal.classfile.TypeKind;
|
import jdk.internal.classfile.TypeKind;
|
||||||
import jdk.internal.classfile.instruction.LocalVariableType;
|
import jdk.internal.classfile.impl.CodeLocalsShifterImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link CodeLocalsShifter} is a {@link CodeTransform} shifting locals to
|
* {@link CodeLocalsShifter} is a {@link CodeTransform} shifting locals to
|
||||||
@ -45,11 +35,11 @@ import jdk.internal.classfile.instruction.LocalVariableType;
|
|||||||
* Locals pointing to the receiver or to method arguments slots are never shifted.
|
* 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.
|
* All locals pointing beyond the method arguments are re-indexed in order of appearance.
|
||||||
*/
|
*/
|
||||||
public sealed interface CodeLocalsShifter extends CodeTransform {
|
public sealed interface CodeLocalsShifter extends CodeTransform permits CodeLocalsShifterImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link CodeLocalsShifter}
|
* Creates a new instance of {@link CodeLocalsShifter}
|
||||||
* with fixed local slots calculated from provided method information
|
* with fixed local slots calculated from provided method information.
|
||||||
* @param methodFlags flags of the method to construct {@link CodeLocalsShifter} for
|
* @param methodFlags flags of the method to construct {@link CodeLocalsShifter} for
|
||||||
* @param methodDescriptor descriptor of the method to construct {@link CodeLocalsShifter} for
|
* @param methodDescriptor descriptor of the method to construct {@link CodeLocalsShifter} for
|
||||||
* @return new instance of {@link CodeLocalsShifter}
|
* @return new instance of {@link CodeLocalsShifter}
|
||||||
@ -60,65 +50,4 @@ public sealed interface CodeLocalsShifter extends CodeTransform {
|
|||||||
fixed += TypeKind.from(param).slotSize();
|
fixed += TypeKind.from(param).slotSize();
|
||||||
return new CodeLocalsShifterImpl(fixed);
|
return new CodeLocalsShifterImpl(fixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
final static class CodeLocalsShifterImpl implements CodeLocalsShifter {
|
|
||||||
|
|
||||||
private int[] locals = new int[0];
|
|
||||||
private final int fixed;
|
|
||||||
|
|
||||||
private CodeLocalsShifterImpl(int fixed) {
|
|
||||||
this.fixed = fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(CodeBuilder cob, CodeElement coe) {
|
|
||||||
switch (coe) {
|
|
||||||
case LoadInstruction li ->
|
|
||||||
cob.loadInstruction(
|
|
||||||
li.typeKind(),
|
|
||||||
shift(cob, li.slot(), li.typeKind()));
|
|
||||||
case StoreInstruction si ->
|
|
||||||
cob.storeInstruction(
|
|
||||||
si.typeKind(),
|
|
||||||
shift(cob, si.slot(), si.typeKind()));
|
|
||||||
case IncrementInstruction ii ->
|
|
||||||
cob.incrementInstruction(
|
|
||||||
shift(cob, ii.slot(), TypeKind.IntType),
|
|
||||||
ii.constant());
|
|
||||||
case LocalVariable lv ->
|
|
||||||
cob.localVariable(
|
|
||||||
shift(cob, lv.slot(), TypeKind.fromDescriptor(lv.type().stringValue())),
|
|
||||||
lv.name(),
|
|
||||||
lv.type(),
|
|
||||||
lv.startScope(),
|
|
||||||
lv.endScope());
|
|
||||||
case LocalVariableType lvt ->
|
|
||||||
cob.localVariableType(
|
|
||||||
shift(cob, lvt.slot(),
|
|
||||||
(lvt.signatureSymbol() instanceof Signature.BaseTypeSig bsig)
|
|
||||||
? TypeKind.fromDescriptor(bsig.signatureString())
|
|
||||||
: TypeKind.ReferenceType),
|
|
||||||
lvt.name(),
|
|
||||||
lvt.signature(),
|
|
||||||
lvt.startScope(),
|
|
||||||
lvt.endScope());
|
|
||||||
default -> cob.with(coe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int shift(CodeBuilder cob, int slot, TypeKind tk) {
|
|
||||||
if (tk == TypeKind.VoidType) throw new IllegalArgumentException("Illegal local void type");
|
|
||||||
if (slot >= fixed) {
|
|
||||||
int key = 2*slot - fixed + tk.slotSize() - 1;
|
|
||||||
if (key >= locals.length) locals = Arrays.copyOf(locals, key + 20);
|
|
||||||
slot = locals[key] - 1;
|
|
||||||
if (slot < 0) {
|
|
||||||
slot = cob.allocateLocal(tk);
|
|
||||||
locals[key] = slot + 1;
|
|
||||||
if (tk.slotSize() == 2) locals[key - 1] = slot + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -19,7 +19,6 @@
|
|||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 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
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
* questions.
|
* questions.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package jdk.internal.classfile.components;
|
package jdk.internal.classfile.components;
|
||||||
|
|
||||||
@ -27,18 +26,9 @@ import java.util.IdentityHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import jdk.internal.classfile.CodeBuilder;
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
import jdk.internal.classfile.CodeElement;
|
|
||||||
import jdk.internal.classfile.CodeTransform;
|
import jdk.internal.classfile.CodeTransform;
|
||||||
import jdk.internal.classfile.instruction.BranchInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.LookupSwitchInstruction;
|
|
||||||
import jdk.internal.classfile.instruction.SwitchCase;
|
|
||||||
import jdk.internal.classfile.instruction.TableSwitchInstruction;
|
|
||||||
import jdk.internal.classfile.Label;
|
import jdk.internal.classfile.Label;
|
||||||
import jdk.internal.classfile.instruction.CharacterRange;
|
import jdk.internal.classfile.impl.CodeRelabelerImpl;
|
||||||
import jdk.internal.classfile.instruction.ExceptionCatch;
|
|
||||||
import jdk.internal.classfile.instruction.LabelTarget;
|
|
||||||
import jdk.internal.classfile.instruction.LocalVariable;
|
|
||||||
import jdk.internal.classfile.instruction.LocalVariableType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A code relabeler is a {@link CodeTransform} replacing all occurrences
|
* A code relabeler is a {@link CodeTransform} replacing all occurrences
|
||||||
@ -50,30 +40,30 @@ import jdk.internal.classfile.instruction.LocalVariableType;
|
|||||||
* Repeated injection of the same code block must be relabeled, so each instance of
|
* Repeated injection of the same code block must be relabeled, so each instance of
|
||||||
* {@link jdk.internal.classfile.Label} is bound in the target bytecode exactly once.
|
* {@link jdk.internal.classfile.Label} is bound in the target bytecode exactly once.
|
||||||
*/
|
*/
|
||||||
public sealed interface CodeRelabeler extends CodeTransform {
|
public sealed interface CodeRelabeler extends CodeTransform permits CodeRelabelerImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of CodeRelabeler
|
* Creates a new instance of CodeRelabeler.
|
||||||
* @return new instance of CodeRelabeler
|
* @return a new instance of CodeRelabeler
|
||||||
*/
|
*/
|
||||||
static CodeRelabeler of() {
|
static CodeRelabeler of() {
|
||||||
return of(new IdentityHashMap<>());
|
return of(new IdentityHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of CodeRelabeler storing the label mapping into the provided map
|
* Creates a new instance of CodeRelabeler storing the label mapping into the provided map.
|
||||||
* @param map label map actively used for relabeling
|
* @param map label map actively used for relabeling
|
||||||
* @return new instance of CodeRelabeler
|
* @return a new instance of CodeRelabeler
|
||||||
*/
|
*/
|
||||||
static CodeRelabeler of(Map<Label, Label> map) {
|
static CodeRelabeler of(Map<Label, Label> map) {
|
||||||
return of((l, cob) -> map.computeIfAbsent(l, ll -> cob.newLabel()));
|
return of((l, cob) -> map.computeIfAbsent(l, ll -> cob.newLabel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of CodeRelabeler using provided {@link java.util.function.BiFunction}
|
* Creates a new instance of CodeRelabeler using provided {@link java.util.function.BiFunction}
|
||||||
* to re-label the code.
|
* to re-label the code.
|
||||||
* @param mapFunction
|
* @param mapFunction
|
||||||
* @return
|
* @return a new instance of CodeRelabeler
|
||||||
*/
|
*/
|
||||||
static CodeRelabeler of(BiFunction<Label, CodeBuilder, Label> mapFunction) {
|
static CodeRelabeler of(BiFunction<Label, CodeBuilder, Label> mapFunction) {
|
||||||
return new CodeRelabelerImpl(mapFunction);
|
return new CodeRelabelerImpl(mapFunction);
|
||||||
@ -86,70 +76,4 @@ public sealed interface CodeRelabeler extends CodeTransform {
|
|||||||
* @return target label
|
* @return target label
|
||||||
*/
|
*/
|
||||||
Label relabel(Label label, CodeBuilder codeBuilder);
|
Label relabel(Label label, CodeBuilder codeBuilder);
|
||||||
|
|
||||||
record CodeRelabelerImpl(BiFunction<Label, CodeBuilder, Label> mapFunction) implements CodeRelabeler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Label relabel(Label label, CodeBuilder cob) {
|
|
||||||
return mapFunction.apply(label, cob);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(CodeBuilder cob, CodeElement coe) {
|
|
||||||
switch (coe) {
|
|
||||||
case BranchInstruction bi ->
|
|
||||||
cob.branchInstruction(
|
|
||||||
bi.opcode(),
|
|
||||||
relabel(bi.target(), cob));
|
|
||||||
case LookupSwitchInstruction lsi ->
|
|
||||||
cob.lookupSwitchInstruction(
|
|
||||||
relabel(lsi.defaultTarget(), cob),
|
|
||||||
lsi.cases().stream().map(c ->
|
|
||||||
SwitchCase.of(
|
|
||||||
c.caseValue(),
|
|
||||||
relabel(c.target(), cob))).toList());
|
|
||||||
case TableSwitchInstruction tsi ->
|
|
||||||
cob.tableSwitchInstruction(
|
|
||||||
tsi.lowValue(),
|
|
||||||
tsi.highValue(),
|
|
||||||
relabel(tsi.defaultTarget(), cob),
|
|
||||||
tsi.cases().stream().map(c ->
|
|
||||||
SwitchCase.of(
|
|
||||||
c.caseValue(),
|
|
||||||
relabel(c.target(), cob))).toList());
|
|
||||||
case LabelTarget lt ->
|
|
||||||
cob.labelBinding(
|
|
||||||
relabel(lt.label(), cob));
|
|
||||||
case ExceptionCatch ec ->
|
|
||||||
cob.exceptionCatch(
|
|
||||||
relabel(ec.tryStart(), cob),
|
|
||||||
relabel(ec.tryEnd(), cob),
|
|
||||||
relabel(ec.handler(), cob),
|
|
||||||
ec.catchType());
|
|
||||||
case LocalVariable lv ->
|
|
||||||
cob.localVariable(
|
|
||||||
lv.slot(),
|
|
||||||
lv.name().stringValue(),
|
|
||||||
lv.typeSymbol(),
|
|
||||||
relabel(lv.startScope(), cob),
|
|
||||||
relabel(lv.endScope(), cob));
|
|
||||||
case LocalVariableType lvt ->
|
|
||||||
cob.localVariableType(
|
|
||||||
lvt.slot(),
|
|
||||||
lvt.name().stringValue(),
|
|
||||||
lvt.signatureSymbol(),
|
|
||||||
relabel(lvt.startScope(), cob),
|
|
||||||
relabel(lvt.endScope(), cob));
|
|
||||||
case CharacterRange chr ->
|
|
||||||
cob.characterRange(
|
|
||||||
relabel(chr.startScope(), cob),
|
|
||||||
relabel(chr.endScope(), cob),
|
|
||||||
chr.characterRangeStart(),
|
|
||||||
chr.characterRangeEnd(),
|
|
||||||
chr.flags());
|
|
||||||
default ->
|
|
||||||
cob.with(coe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -19,25 +19,15 @@
|
|||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 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
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
* questions.
|
* questions.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package jdk.internal.classfile.components;
|
package jdk.internal.classfile.components;
|
||||||
|
|
||||||
import java.util.AbstractCollection;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import jdk.internal.classfile.CodeBuilder;
|
|
||||||
import jdk.internal.classfile.CodeElement;
|
|
||||||
import jdk.internal.classfile.CodeTransform;
|
import jdk.internal.classfile.CodeTransform;
|
||||||
import jdk.internal.classfile.Label;
|
import jdk.internal.classfile.Label;
|
||||||
import jdk.internal.classfile.Opcode;
|
|
||||||
import jdk.internal.classfile.TypeKind;
|
import jdk.internal.classfile.TypeKind;
|
||||||
import jdk.internal.classfile.instruction.*;
|
import jdk.internal.classfile.impl.CodeStackTrackerImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link CodeStackTracker} is a {@link CodeTransform} tracking stack content
|
* {@link CodeStackTracker} is a {@link CodeTransform} tracking stack content
|
||||||
@ -57,10 +47,10 @@ import jdk.internal.classfile.instruction.*;
|
|||||||
* });
|
* });
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public sealed interface CodeStackTracker extends CodeTransform {
|
public sealed interface CodeStackTracker extends CodeTransform permits CodeStackTrackerImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of {@link CodeStackTracker} initialized with provided stack items
|
* Creates new instance of {@link CodeStackTracker} initialized with provided stack items.
|
||||||
* @param initialStack initial stack content
|
* @param initialStack initial stack content
|
||||||
* @return new instance of {@link CodeStackTracker}
|
* @return new instance of {@link CodeStackTracker}
|
||||||
*/
|
*/
|
||||||
@ -72,7 +62,7 @@ public sealed interface CodeStackTracker extends CodeTransform {
|
|||||||
* Returns {@linkplain Collection} of {@linkplain TypeKind} representing current stack.
|
* Returns {@linkplain Collection} of {@linkplain TypeKind} representing current stack.
|
||||||
* Returns an empty {@linkplain Optional} when the Stack content is unknown
|
* Returns an empty {@linkplain Optional} when the Stack content is unknown
|
||||||
* (right after {@code xRETURN, ATHROW, GOTO, GOTO_W, LOOKUPSWITCH, TABLESWITCH} instructions).
|
* (right after {@code xRETURN, ATHROW, GOTO, GOTO_W, LOOKUPSWITCH, TABLESWITCH} instructions).
|
||||||
*
|
* <p>
|
||||||
* Temporary unknown stack content can be recovered by binding of a {@linkplain Label} used as
|
* Temporary unknown stack content can be recovered by binding of a {@linkplain Label} used as
|
||||||
* target of a branch instruction from existing code with known stack (forward branch target),
|
* target of a branch instruction from existing code with known stack (forward branch target),
|
||||||
* or by binding of a {@linkplain Label} defining an exception handler (exception handler code start).
|
* or by binding of a {@linkplain Label} defining an exception handler (exception handler code start).
|
||||||
@ -84,291 +74,11 @@ public sealed interface CodeStackTracker extends CodeTransform {
|
|||||||
/**
|
/**
|
||||||
* Returns tracked max stack size.
|
* Returns tracked max stack size.
|
||||||
* Returns an empty {@linkplain Optional} when max stack size tracking has been lost.
|
* Returns an empty {@linkplain Optional} when max stack size tracking has been lost.
|
||||||
*
|
* <p>
|
||||||
* Max stack size tracking is permanently lost when a stack instruction appears
|
* Max stack size tracking is permanently lost when a stack instruction appears
|
||||||
* and the actual stack content is unknown.
|
* and the actual stack content is unknown.
|
||||||
*
|
*
|
||||||
* @return tracked max stack size, or an empty {@linkplain Optional} if tracking has been lost
|
* @return tracked max stack size, or an empty {@linkplain Optional} if tracking has been lost
|
||||||
*/
|
*/
|
||||||
Optional<Integer> maxStackSize();
|
Optional<Integer> maxStackSize();
|
||||||
|
|
||||||
final static class CodeStackTrackerImpl implements CodeStackTracker {
|
|
||||||
|
|
||||||
private static record Item(TypeKind type, Item next) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class Stack extends AbstractCollection<TypeKind> {
|
|
||||||
|
|
||||||
private Item top;
|
|
||||||
private int count, realSize;
|
|
||||||
|
|
||||||
Stack(Item top, int count, int realSize) {
|
|
||||||
this.top = top;
|
|
||||||
this.count = count;
|
|
||||||
this.realSize = realSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<TypeKind> iterator() {
|
|
||||||
return new Iterator<TypeKind>() {
|
|
||||||
Item i = top;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return i != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TypeKind next() {
|
|
||||||
if (i == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
var t = i.type;
|
|
||||||
i = i.next;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void push(TypeKind type) {
|
|
||||||
top = new Item(type, top);
|
|
||||||
realSize += type.slotSize();
|
|
||||||
count++;
|
|
||||||
if (maxSize != null && realSize > maxSize) maxSize = realSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeKind pop() {
|
|
||||||
var t = top.type;
|
|
||||||
realSize -= t.slotSize();
|
|
||||||
count--;
|
|
||||||
top = top.next;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stack stack = new Stack(null, 0, 0);
|
|
||||||
private Integer maxSize = 0;
|
|
||||||
|
|
||||||
CodeStackTrackerImpl(TypeKind... initialStack) {
|
|
||||||
for (int i = initialStack.length - 1; i >= 0; i--)
|
|
||||||
push(initialStack[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Collection<TypeKind>> stack() {
|
|
||||||
return Optional.ofNullable(fork());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Integer> maxStackSize() {
|
|
||||||
return Optional.ofNullable(maxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<Label, Stack> map = new HashMap<>();
|
|
||||||
|
|
||||||
private void push(TypeKind type) {
|
|
||||||
if (stack != null) {
|
|
||||||
if (type != TypeKind.VoidType) stack.push(type);
|
|
||||||
} else {
|
|
||||||
maxSize = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pop(int i) {
|
|
||||||
if (stack != null) {
|
|
||||||
while (i-- > 0) stack.pop();
|
|
||||||
} else {
|
|
||||||
maxSize = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stack fork() {
|
|
||||||
return stack == null ? null : new Stack(stack.top, stack.count, stack.realSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void withStack(Consumer<Stack> c) {
|
|
||||||
if (stack != null) c.accept(stack);
|
|
||||||
else maxSize = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(CodeBuilder cb, CodeElement el) {
|
|
||||||
cb.with(el);
|
|
||||||
switch (el) {
|
|
||||||
case ArrayLoadInstruction i -> {
|
|
||||||
pop(2);push(i.typeKind());
|
|
||||||
}
|
|
||||||
case ArrayStoreInstruction i ->
|
|
||||||
pop(3);
|
|
||||||
case BranchInstruction i -> {
|
|
||||||
if (i.opcode() == Opcode.GOTO || i.opcode() == Opcode.GOTO_W) {
|
|
||||||
map.put(i.target(), stack);
|
|
||||||
stack = null;
|
|
||||||
} else {
|
|
||||||
pop(1);
|
|
||||||
map.put(i.target(), fork());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ConstantInstruction i ->
|
|
||||||
push(i.typeKind());
|
|
||||||
case ConvertInstruction i -> {
|
|
||||||
pop(1);push(i.toType());
|
|
||||||
}
|
|
||||||
case FieldInstruction i -> {
|
|
||||||
switch (i.opcode()) {
|
|
||||||
case GETSTATIC ->
|
|
||||||
push(TypeKind.fromDescriptor(i.type().stringValue()));
|
|
||||||
case GETFIELD -> {
|
|
||||||
pop(1);push(TypeKind.fromDescriptor(i.type().stringValue()));
|
|
||||||
}
|
|
||||||
case PUTSTATIC ->
|
|
||||||
pop(1);
|
|
||||||
case PUTFIELD ->
|
|
||||||
pop(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case InvokeDynamicInstruction i -> {
|
|
||||||
var type = i.typeSymbol();
|
|
||||||
pop(type.parameterCount());
|
|
||||||
push(TypeKind.from(type.returnType()));
|
|
||||||
}
|
|
||||||
case InvokeInstruction i -> {
|
|
||||||
var type = i.typeSymbol();
|
|
||||||
pop(type.parameterCount());
|
|
||||||
if (i.opcode() != Opcode.INVOKESTATIC) pop(1);
|
|
||||||
push(TypeKind.from(type.returnType()));
|
|
||||||
}
|
|
||||||
case LoadInstruction i ->
|
|
||||||
push(i.typeKind());
|
|
||||||
case StoreInstruction i ->
|
|
||||||
pop(1);
|
|
||||||
case LookupSwitchInstruction i -> {
|
|
||||||
map.put(i.defaultTarget(), stack);
|
|
||||||
for (var c : i.cases()) map.put(c.target(), fork());
|
|
||||||
stack = null;
|
|
||||||
}
|
|
||||||
case MonitorInstruction i ->
|
|
||||||
pop(1);
|
|
||||||
case NewMultiArrayInstruction i -> {
|
|
||||||
pop(i.dimensions());push(TypeKind.ReferenceType);
|
|
||||||
}
|
|
||||||
case NewObjectInstruction i ->
|
|
||||||
push(TypeKind.ReferenceType);
|
|
||||||
case NewPrimitiveArrayInstruction i -> {
|
|
||||||
pop(1);push(TypeKind.ReferenceType);
|
|
||||||
}
|
|
||||||
case NewReferenceArrayInstruction i -> {
|
|
||||||
pop(1);push(TypeKind.ReferenceType);
|
|
||||||
}
|
|
||||||
case NopInstruction i -> {}
|
|
||||||
case OperatorInstruction i -> {
|
|
||||||
switch (i.opcode()) {
|
|
||||||
case ARRAYLENGTH, INEG, LNEG, FNEG, DNEG -> pop(1);
|
|
||||||
default -> pop(2);
|
|
||||||
}
|
|
||||||
push(i.typeKind());
|
|
||||||
}
|
|
||||||
case ReturnInstruction i ->
|
|
||||||
stack = null;
|
|
||||||
case StackInstruction i -> {
|
|
||||||
switch (i.opcode()) {
|
|
||||||
case POP -> pop(1);
|
|
||||||
case POP2 -> withStack(s -> {
|
|
||||||
if (s.pop().slotSize() == 1) s.pop();
|
|
||||||
});
|
|
||||||
case DUP -> withStack(s -> {
|
|
||||||
var v = s.pop();s.push(v);s.push(v);
|
|
||||||
});
|
|
||||||
case DUP2 -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
if (v1.slotSize() == 1) {
|
|
||||||
var v2 = s.pop();
|
|
||||||
s.push(v2);s.push(v1);
|
|
||||||
s.push(v2);s.push(v1);
|
|
||||||
} else {
|
|
||||||
s.push(v1);s.push(v1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
case DUP_X1 -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
var v2 = s.pop();
|
|
||||||
s.push(v1);s.push(v2);s.push(v1);
|
|
||||||
});
|
|
||||||
case DUP_X2 -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
var v2 = s.pop();
|
|
||||||
if (v2.slotSize() == 1) {
|
|
||||||
var v3 = s.pop();
|
|
||||||
s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
|
||||||
} else {
|
|
||||||
s.push(v1);s.push(v2);s.push(v1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
case DUP2_X1 -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
var v2 = s.pop();
|
|
||||||
if (v1.slotSize() == 1) {
|
|
||||||
var v3 = s.pop();
|
|
||||||
s.push(v2);s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
|
||||||
} else {
|
|
||||||
s.push(v1);s.push(v2);s.push(v1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
case DUP2_X2 -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
var v2 = s.pop();
|
|
||||||
if (v1.slotSize() == 1) {
|
|
||||||
var v3 = s.pop();
|
|
||||||
if (v3.slotSize() == 1) {
|
|
||||||
var v4 = s.pop();
|
|
||||||
s.push(v2);s.push(v1);s.push(v4);s.push(v3);s.push(v2);s.push(v1);
|
|
||||||
} else {
|
|
||||||
s.push(v2);s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (v2.slotSize() == 1) {
|
|
||||||
var v3 = s.pop();
|
|
||||||
s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
|
||||||
} else {
|
|
||||||
s.push(v1);s.push(v2);s.push(v1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
case SWAP -> withStack(s -> {
|
|
||||||
var v1 = s.pop();
|
|
||||||
var v2 = s.pop();
|
|
||||||
s.push(v1);s.push(v2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case TableSwitchInstruction i -> {
|
|
||||||
map.put(i.defaultTarget(), stack);
|
|
||||||
for (var c : i.cases()) map.put(c.target(), fork());
|
|
||||||
stack = null;
|
|
||||||
}
|
|
||||||
case ThrowInstruction i ->
|
|
||||||
stack = null;
|
|
||||||
case TypeCheckInstruction i -> {
|
|
||||||
switch (i.opcode()) {
|
|
||||||
case CHECKCAST -> {
|
|
||||||
pop(1);push(TypeKind.ReferenceType);
|
|
||||||
}
|
|
||||||
case INSTANCEOF -> {
|
|
||||||
pop(1);push(TypeKind.IntType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ExceptionCatch i ->
|
|
||||||
map.put(i.handler(), new Stack(new Item(TypeKind.ReferenceType, null), 1, 1));
|
|
||||||
case LabelTarget i ->
|
|
||||||
stack = map.getOrDefault(i.label(), stack);
|
|
||||||
default -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,416 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package jdk.internal.classfile.impl;
|
||||||
|
|
||||||
|
import jdk.internal.classfile.Annotation;
|
||||||
|
import jdk.internal.classfile.AnnotationElement;
|
||||||
|
import jdk.internal.classfile.AnnotationValue;
|
||||||
|
import jdk.internal.classfile.ClassBuilder;
|
||||||
|
import jdk.internal.classfile.ClassElement;
|
||||||
|
import jdk.internal.classfile.ClassSignature;
|
||||||
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
|
import jdk.internal.classfile.CodeElement;
|
||||||
|
import jdk.internal.classfile.CodeModel;
|
||||||
|
import jdk.internal.classfile.CodeTransform;
|
||||||
|
import jdk.internal.classfile.FieldBuilder;
|
||||||
|
import jdk.internal.classfile.FieldElement;
|
||||||
|
import jdk.internal.classfile.FieldModel;
|
||||||
|
import jdk.internal.classfile.FieldTransform;
|
||||||
|
import jdk.internal.classfile.Interfaces;
|
||||||
|
import jdk.internal.classfile.MethodBuilder;
|
||||||
|
import jdk.internal.classfile.MethodElement;
|
||||||
|
import jdk.internal.classfile.MethodModel;
|
||||||
|
import jdk.internal.classfile.MethodSignature;
|
||||||
|
import jdk.internal.classfile.MethodTransform;
|
||||||
|
import jdk.internal.classfile.Signature;
|
||||||
|
import jdk.internal.classfile.Superclass;
|
||||||
|
import jdk.internal.classfile.TypeAnnotation;
|
||||||
|
import jdk.internal.classfile.attribute.AnnotationDefaultAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.EnclosingMethodAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.ExceptionsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.InnerClassInfo;
|
||||||
|
import jdk.internal.classfile.attribute.InnerClassesAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.ModuleAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.ModuleProvideInfo;
|
||||||
|
import jdk.internal.classfile.attribute.NestHostAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.NestMembersAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.PermittedSubclassesAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RecordAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RecordComponentInfo;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
|
||||||
|
import jdk.internal.classfile.attribute.SignatureAttribute;
|
||||||
|
import jdk.internal.classfile.components.ClassRemapper;
|
||||||
|
import jdk.internal.classfile.constantpool.Utf8Entry;
|
||||||
|
import jdk.internal.classfile.instruction.ConstantInstruction.LoadConstantInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ExceptionCatch;
|
||||||
|
import jdk.internal.classfile.instruction.FieldInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.InvokeDynamicInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.InvokeInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariable;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariableType;
|
||||||
|
import jdk.internal.classfile.instruction.NewMultiArrayInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewObjectInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewReferenceArrayInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.TypeCheckInstruction;
|
||||||
|
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import java.lang.constant.ConstantDesc;
|
||||||
|
import java.lang.constant.DirectMethodHandleDesc;
|
||||||
|
import java.lang.constant.DynamicCallSiteDesc;
|
||||||
|
import java.lang.constant.DynamicConstantDesc;
|
||||||
|
import java.lang.constant.MethodHandleDesc;
|
||||||
|
import java.lang.constant.MethodTypeDesc;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public record ClassRemapperImpl(Function<ClassDesc, ClassDesc> mapFunction) implements ClassRemapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(ClassBuilder clb, ClassElement cle) {
|
||||||
|
switch (cle) {
|
||||||
|
case FieldModel fm ->
|
||||||
|
clb.withField(fm.fieldName().stringValue(), map(
|
||||||
|
fm.fieldTypeSymbol()), fb ->
|
||||||
|
fm.forEachElement(asFieldTransform().resolve(fb).consumer()));
|
||||||
|
case MethodModel mm ->
|
||||||
|
clb.withMethod(mm.methodName().stringValue(), mapMethodDesc(
|
||||||
|
mm.methodTypeSymbol()), mm.flags().flagsMask(), mb ->
|
||||||
|
mm.forEachElement(asMethodTransform().resolve(mb).consumer()));
|
||||||
|
case Superclass sc ->
|
||||||
|
clb.withSuperclass(map(sc.superclassEntry().asSymbol()));
|
||||||
|
case Interfaces ins ->
|
||||||
|
clb.withInterfaceSymbols(Util.mappedList(ins.interfaces(), in ->
|
||||||
|
map(in.asSymbol())));
|
||||||
|
case SignatureAttribute sa ->
|
||||||
|
clb.with(SignatureAttribute.of(mapClassSignature(sa.asClassSignature())));
|
||||||
|
case InnerClassesAttribute ica ->
|
||||||
|
clb.with(InnerClassesAttribute.of(ica.classes().stream().map(ici ->
|
||||||
|
InnerClassInfo.of(map(ici.innerClass().asSymbol()),
|
||||||
|
ici.outerClass().map(oc -> map(oc.asSymbol())),
|
||||||
|
ici.innerName().map(Utf8Entry::stringValue),
|
||||||
|
ici.flagsMask())).toList()));
|
||||||
|
case EnclosingMethodAttribute ema ->
|
||||||
|
clb.with(EnclosingMethodAttribute.of(map(ema.enclosingClass().asSymbol()),
|
||||||
|
ema.enclosingMethodName().map(Utf8Entry::stringValue),
|
||||||
|
ema.enclosingMethodTypeSymbol().map(this::mapMethodDesc)));
|
||||||
|
case RecordAttribute ra ->
|
||||||
|
clb.with(RecordAttribute.of(ra.components().stream()
|
||||||
|
.map(this::mapRecordComponent).toList()));
|
||||||
|
case ModuleAttribute ma ->
|
||||||
|
clb.with(ModuleAttribute.of(ma.moduleName(), ma.moduleFlagsMask(),
|
||||||
|
ma.moduleVersion().orElse(null),
|
||||||
|
ma.requires(), ma.exports(), ma.opens(),
|
||||||
|
ma.uses().stream().map(ce ->
|
||||||
|
clb.constantPool().classEntry(map(ce.asSymbol()))).toList(),
|
||||||
|
ma.provides().stream().map(mp ->
|
||||||
|
ModuleProvideInfo.of(map(mp.provides().asSymbol()),
|
||||||
|
mp.providesWith().stream().map(pw ->
|
||||||
|
map(pw.asSymbol())).toList())).toList()));
|
||||||
|
case NestHostAttribute nha ->
|
||||||
|
clb.with(NestHostAttribute.of(map(nha.nestHost().asSymbol())));
|
||||||
|
case NestMembersAttribute nma ->
|
||||||
|
clb.with(NestMembersAttribute.ofSymbols(nma.nestMembers().stream()
|
||||||
|
.map(nm -> map(nm.asSymbol())).toList()));
|
||||||
|
case PermittedSubclassesAttribute psa ->
|
||||||
|
clb.with(PermittedSubclassesAttribute.ofSymbols(
|
||||||
|
psa.permittedSubclasses().stream().map(ps ->
|
||||||
|
map(ps.asSymbol())).toList()));
|
||||||
|
case RuntimeVisibleAnnotationsAttribute aa ->
|
||||||
|
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleAnnotationsAttribute aa ->
|
||||||
|
clb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
||||||
|
clb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
||||||
|
clb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
default ->
|
||||||
|
clb.with(cle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldTransform asFieldTransform() {
|
||||||
|
return (FieldBuilder fb, FieldElement fe) -> {
|
||||||
|
switch (fe) {
|
||||||
|
case SignatureAttribute sa ->
|
||||||
|
fb.with(SignatureAttribute.of(
|
||||||
|
mapSignature(sa.asTypeSignature())));
|
||||||
|
case RuntimeVisibleAnnotationsAttribute aa ->
|
||||||
|
fb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleAnnotationsAttribute aa ->
|
||||||
|
fb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
||||||
|
fb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
||||||
|
fb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
default ->
|
||||||
|
fb.with(fe);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodTransform asMethodTransform() {
|
||||||
|
return (MethodBuilder mb, MethodElement me) -> {
|
||||||
|
switch (me) {
|
||||||
|
case AnnotationDefaultAttribute ada ->
|
||||||
|
mb.with(AnnotationDefaultAttribute.of(
|
||||||
|
mapAnnotationValue(ada.defaultValue())));
|
||||||
|
case CodeModel com ->
|
||||||
|
mb.transformCode(com, asCodeTransform());
|
||||||
|
case ExceptionsAttribute ea ->
|
||||||
|
mb.with(ExceptionsAttribute.ofSymbols(
|
||||||
|
ea.exceptions().stream().map(ce ->
|
||||||
|
map(ce.asSymbol())).toList()));
|
||||||
|
case SignatureAttribute sa ->
|
||||||
|
mb.with(SignatureAttribute.of(
|
||||||
|
mapMethodSignature(sa.asMethodSignature())));
|
||||||
|
case RuntimeVisibleAnnotationsAttribute aa ->
|
||||||
|
mb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleAnnotationsAttribute aa ->
|
||||||
|
mb.with(RuntimeInvisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations())));
|
||||||
|
case RuntimeVisibleParameterAnnotationsAttribute paa ->
|
||||||
|
mb.with(RuntimeVisibleParameterAnnotationsAttribute.of(
|
||||||
|
paa.parameterAnnotations().stream()
|
||||||
|
.map(this::mapAnnotations).toList()));
|
||||||
|
case RuntimeInvisibleParameterAnnotationsAttribute paa ->
|
||||||
|
mb.with(RuntimeInvisibleParameterAnnotationsAttribute.of(
|
||||||
|
paa.parameterAnnotations().stream()
|
||||||
|
.map(this::mapAnnotations).toList()));
|
||||||
|
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
||||||
|
mb.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
||||||
|
mb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
default ->
|
||||||
|
mb.with(me);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeTransform asCodeTransform() {
|
||||||
|
return (CodeBuilder cob, CodeElement coe) -> {
|
||||||
|
switch (coe) {
|
||||||
|
case FieldInstruction fai ->
|
||||||
|
cob.fieldInstruction(fai.opcode(), map(fai.owner().asSymbol()),
|
||||||
|
fai.name().stringValue(), map(fai.typeSymbol()));
|
||||||
|
case InvokeInstruction ii ->
|
||||||
|
cob.invokeInstruction(ii.opcode(), map(ii.owner().asSymbol()),
|
||||||
|
ii.name().stringValue(), mapMethodDesc(ii.typeSymbol()),
|
||||||
|
ii.isInterface());
|
||||||
|
case InvokeDynamicInstruction idi ->
|
||||||
|
cob.invokeDynamicInstruction(DynamicCallSiteDesc.of(
|
||||||
|
idi.bootstrapMethod(), idi.name().stringValue(),
|
||||||
|
mapMethodDesc(idi.typeSymbol()),
|
||||||
|
idi.bootstrapArgs().stream().map(this::mapConstantValue).toArray(ConstantDesc[]::new)));
|
||||||
|
case NewObjectInstruction c ->
|
||||||
|
cob.newObjectInstruction(map(c.className().asSymbol()));
|
||||||
|
case NewReferenceArrayInstruction c ->
|
||||||
|
cob.anewarray(map(c.componentType().asSymbol()));
|
||||||
|
case NewMultiArrayInstruction c ->
|
||||||
|
cob.multianewarray(map(c.arrayType().asSymbol()), c.dimensions());
|
||||||
|
case TypeCheckInstruction c ->
|
||||||
|
cob.typeCheckInstruction(c.opcode(), map(c.type().asSymbol()));
|
||||||
|
case ExceptionCatch c ->
|
||||||
|
cob.exceptionCatch(c.tryStart(), c.tryEnd(), c.handler(),c.catchType()
|
||||||
|
.map(d -> TemporaryConstantPool.INSTANCE.classEntry(map(d.asSymbol()))));
|
||||||
|
case LocalVariable c ->
|
||||||
|
cob.localVariable(c.slot(), c.name().stringValue(), map(c.typeSymbol()),
|
||||||
|
c.startScope(), c.endScope());
|
||||||
|
case LocalVariableType c ->
|
||||||
|
cob.localVariableType(c.slot(), c.name().stringValue(),
|
||||||
|
mapSignature(c.signatureSymbol()), c.startScope(), c.endScope());
|
||||||
|
case LoadConstantInstruction ldc ->
|
||||||
|
cob.constantInstruction(ldc.opcode(),
|
||||||
|
mapConstantValue(ldc.constantValue()));
|
||||||
|
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
||||||
|
cob.with(RuntimeVisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
||||||
|
cob.with(RuntimeInvisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations())));
|
||||||
|
default ->
|
||||||
|
cob.with(coe);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassDesc map(ClassDesc desc) {
|
||||||
|
if (desc == null) return null;
|
||||||
|
if (desc.isArray()) return map(desc.componentType()).arrayType();
|
||||||
|
if (desc.isPrimitive()) return desc;
|
||||||
|
return mapFunction.apply(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodTypeDesc mapMethodDesc(MethodTypeDesc desc) {
|
||||||
|
return MethodTypeDesc.of(map(desc.returnType()),
|
||||||
|
desc.parameterList().stream().map(this::map).toArray(ClassDesc[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassSignature mapClassSignature(ClassSignature signature) {
|
||||||
|
return ClassSignature.of(mapTypeParams(signature.typeParameters()),
|
||||||
|
mapSignature(signature.superclassSignature()),
|
||||||
|
signature.superinterfaceSignatures().stream()
|
||||||
|
.map(this::mapSignature).toArray(Signature.RefTypeSig[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodSignature mapMethodSignature(MethodSignature signature) {
|
||||||
|
return MethodSignature.of(mapTypeParams(signature.typeParameters()),
|
||||||
|
signature.throwableSignatures().stream().map(this::mapSignature).toList(),
|
||||||
|
mapSignature(signature.result()),
|
||||||
|
signature.arguments().stream()
|
||||||
|
.map(this::mapSignature).toArray(Signature[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordComponentInfo mapRecordComponent(RecordComponentInfo component) {
|
||||||
|
return RecordComponentInfo.of(component.name().stringValue(),
|
||||||
|
map(component.descriptorSymbol()),
|
||||||
|
component.attributes().stream().map(atr ->
|
||||||
|
switch (atr) {
|
||||||
|
case SignatureAttribute sa ->
|
||||||
|
SignatureAttribute.of(
|
||||||
|
mapSignature(sa.asTypeSignature()));
|
||||||
|
case RuntimeVisibleAnnotationsAttribute aa ->
|
||||||
|
RuntimeVisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations()));
|
||||||
|
case RuntimeInvisibleAnnotationsAttribute aa ->
|
||||||
|
RuntimeInvisibleAnnotationsAttribute.of(
|
||||||
|
mapAnnotations(aa.annotations()));
|
||||||
|
case RuntimeVisibleTypeAnnotationsAttribute aa ->
|
||||||
|
RuntimeVisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations()));
|
||||||
|
case RuntimeInvisibleTypeAnnotationsAttribute aa ->
|
||||||
|
RuntimeInvisibleTypeAnnotationsAttribute.of(
|
||||||
|
mapTypeAnnotations(aa.annotations()));
|
||||||
|
default -> atr;
|
||||||
|
}).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectMethodHandleDesc mapDirectMethodHandle(DirectMethodHandleDesc dmhd) {
|
||||||
|
return switch (dmhd.kind()) {
|
||||||
|
case GETTER, SETTER, STATIC_GETTER, STATIC_SETTER ->
|
||||||
|
MethodHandleDesc.ofField(dmhd.kind(), map(dmhd.owner()),
|
||||||
|
dmhd.methodName(),
|
||||||
|
map(ClassDesc.ofDescriptor(dmhd.lookupDescriptor())));
|
||||||
|
default ->
|
||||||
|
MethodHandleDesc.ofMethod(dmhd.kind(), map(dmhd.owner()),
|
||||||
|
dmhd.methodName(),
|
||||||
|
mapMethodDesc(MethodTypeDesc.ofDescriptor(dmhd.lookupDescriptor())));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantDesc mapConstantValue(ConstantDesc value) {
|
||||||
|
return switch (value) {
|
||||||
|
case ClassDesc cd ->
|
||||||
|
map(cd);
|
||||||
|
case DynamicConstantDesc<?> dcd ->
|
||||||
|
mapDynamicConstant(dcd);
|
||||||
|
case DirectMethodHandleDesc dmhd ->
|
||||||
|
mapDirectMethodHandle(dmhd);
|
||||||
|
case MethodTypeDesc mtd ->
|
||||||
|
mapMethodDesc(mtd);
|
||||||
|
default -> value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicConstantDesc<?> mapDynamicConstant(DynamicConstantDesc<?> dcd) {
|
||||||
|
return DynamicConstantDesc.ofNamed(mapDirectMethodHandle(dcd.bootstrapMethod()),
|
||||||
|
dcd.constantName(),
|
||||||
|
map(dcd.constantType()),
|
||||||
|
dcd.bootstrapArgsList().stream().map(this::mapConstantValue).toArray(ConstantDesc[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
<S extends Signature> S mapSignature(S signature) {
|
||||||
|
return (S) switch (signature) {
|
||||||
|
case Signature.ArrayTypeSig ats ->
|
||||||
|
Signature.ArrayTypeSig.of(mapSignature(ats.componentSignature()));
|
||||||
|
case Signature.ClassTypeSig cts ->
|
||||||
|
Signature.ClassTypeSig.of(
|
||||||
|
cts.outerType().map(this::mapSignature).orElse(null),
|
||||||
|
map(cts.classDesc()),
|
||||||
|
cts.typeArgs().stream()
|
||||||
|
.map(ta -> Signature.TypeArg.of(
|
||||||
|
ta.wildcardIndicator(),
|
||||||
|
ta.boundType().map(this::mapSignature)))
|
||||||
|
.toArray(Signature.TypeArg[]::new));
|
||||||
|
default -> signature;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Annotation> mapAnnotations(List<Annotation> annotations) {
|
||||||
|
return annotations.stream().map(this::mapAnnotation).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotation mapAnnotation(Annotation a) {
|
||||||
|
return Annotation.of(map(a.classSymbol()), a.elements().stream().map(el ->
|
||||||
|
AnnotationElement.of(el.name(), mapAnnotationValue(el.value()))).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationValue mapAnnotationValue(AnnotationValue val) {
|
||||||
|
return switch (val) {
|
||||||
|
case AnnotationValue.OfAnnotation oa ->
|
||||||
|
AnnotationValue.ofAnnotation(mapAnnotation(oa.annotation()));
|
||||||
|
case AnnotationValue.OfArray oa ->
|
||||||
|
AnnotationValue.ofArray(oa.values().stream().map(this::mapAnnotationValue).toList());
|
||||||
|
case AnnotationValue.OfConstant oc -> oc;
|
||||||
|
case AnnotationValue.OfClass oc ->
|
||||||
|
AnnotationValue.ofClass(map(oc.classSymbol()));
|
||||||
|
case AnnotationValue.OfEnum oe ->
|
||||||
|
AnnotationValue.ofEnum(map(oe.classSymbol()), oe.constantName().stringValue());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TypeAnnotation> mapTypeAnnotations(List<TypeAnnotation> typeAnnotations) {
|
||||||
|
return typeAnnotations.stream().map(a -> TypeAnnotation.of(a.targetInfo(),
|
||||||
|
a.targetPath(), map(a.classSymbol()),
|
||||||
|
a.elements().stream().map(el -> AnnotationElement.of(el.name(),
|
||||||
|
mapAnnotationValue(el.value()))).toList())).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Signature.TypeParam> mapTypeParams(List<Signature.TypeParam> typeParams) {
|
||||||
|
return typeParams.stream().map(tp -> Signature.TypeParam.of(tp.identifier(),
|
||||||
|
tp.classBound().map(this::mapSignature),
|
||||||
|
tp.interfaceBounds().stream()
|
||||||
|
.map(this::mapSignature).toArray(Signature.RefTypeSig[]::new))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package jdk.internal.classfile.impl;
|
||||||
|
|
||||||
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
|
import jdk.internal.classfile.CodeElement;
|
||||||
|
import jdk.internal.classfile.Signature;
|
||||||
|
import jdk.internal.classfile.TypeKind;
|
||||||
|
import jdk.internal.classfile.components.CodeLocalsShifter;
|
||||||
|
import jdk.internal.classfile.instruction.IncrementInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.LoadInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariable;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariableType;
|
||||||
|
import jdk.internal.classfile.instruction.StoreInstruction;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class CodeLocalsShifterImpl implements CodeLocalsShifter {
|
||||||
|
|
||||||
|
private int[] locals = new int[0];
|
||||||
|
private final int fixed;
|
||||||
|
|
||||||
|
public CodeLocalsShifterImpl(int fixed) {
|
||||||
|
this.fixed = fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(CodeBuilder cob, CodeElement coe) {
|
||||||
|
switch (coe) {
|
||||||
|
case LoadInstruction li ->
|
||||||
|
cob.loadInstruction(
|
||||||
|
li.typeKind(),
|
||||||
|
shift(cob, li.slot(), li.typeKind()));
|
||||||
|
case StoreInstruction si ->
|
||||||
|
cob.storeInstruction(
|
||||||
|
si.typeKind(),
|
||||||
|
shift(cob, si.slot(), si.typeKind()));
|
||||||
|
case IncrementInstruction ii ->
|
||||||
|
cob.incrementInstruction(
|
||||||
|
shift(cob, ii.slot(), TypeKind.IntType),
|
||||||
|
ii.constant());
|
||||||
|
case LocalVariable lv ->
|
||||||
|
cob.localVariable(
|
||||||
|
shift(cob, lv.slot(), TypeKind.fromDescriptor(lv.type().stringValue())),
|
||||||
|
lv.name(),
|
||||||
|
lv.type(),
|
||||||
|
lv.startScope(),
|
||||||
|
lv.endScope());
|
||||||
|
case LocalVariableType lvt ->
|
||||||
|
cob.localVariableType(
|
||||||
|
shift(cob, lvt.slot(),
|
||||||
|
(lvt.signatureSymbol() instanceof Signature.BaseTypeSig bsig)
|
||||||
|
? TypeKind.fromDescriptor(bsig.signatureString())
|
||||||
|
: TypeKind.ReferenceType),
|
||||||
|
lvt.name(),
|
||||||
|
lvt.signature(),
|
||||||
|
lvt.startScope(),
|
||||||
|
lvt.endScope());
|
||||||
|
default -> cob.with(coe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int shift(CodeBuilder cob, int slot, TypeKind tk) {
|
||||||
|
if (tk == TypeKind.VoidType) throw new IllegalArgumentException("Illegal local void type");
|
||||||
|
if (slot >= fixed) {
|
||||||
|
int key = 2*slot - fixed + tk.slotSize() - 1;
|
||||||
|
if (key >= locals.length) locals = Arrays.copyOf(locals, key + 20);
|
||||||
|
slot = locals[key] - 1;
|
||||||
|
if (slot < 0) {
|
||||||
|
slot = cob.allocateLocal(tk);
|
||||||
|
locals[key] = slot + 1;
|
||||||
|
if (tk.slotSize() == 2) locals[key - 1] = slot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package jdk.internal.classfile.impl;
|
||||||
|
|
||||||
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
|
import jdk.internal.classfile.CodeElement;
|
||||||
|
import jdk.internal.classfile.Label;
|
||||||
|
import jdk.internal.classfile.components.CodeRelabeler;
|
||||||
|
import jdk.internal.classfile.instruction.BranchInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.CharacterRange;
|
||||||
|
import jdk.internal.classfile.instruction.ExceptionCatch;
|
||||||
|
import jdk.internal.classfile.instruction.LabelTarget;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariable;
|
||||||
|
import jdk.internal.classfile.instruction.LocalVariableType;
|
||||||
|
import jdk.internal.classfile.instruction.LookupSwitchInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.SwitchCase;
|
||||||
|
import jdk.internal.classfile.instruction.TableSwitchInstruction;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public record CodeRelabelerImpl(BiFunction<Label, CodeBuilder, Label> mapFunction) implements CodeRelabeler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Label relabel(Label label, CodeBuilder cob) {
|
||||||
|
return mapFunction.apply(label, cob);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(CodeBuilder cob, CodeElement coe) {
|
||||||
|
switch (coe) {
|
||||||
|
case BranchInstruction bi ->
|
||||||
|
cob.branchInstruction(
|
||||||
|
bi.opcode(),
|
||||||
|
relabel(bi.target(), cob));
|
||||||
|
case LookupSwitchInstruction lsi ->
|
||||||
|
cob.lookupSwitchInstruction(
|
||||||
|
relabel(lsi.defaultTarget(), cob),
|
||||||
|
lsi.cases().stream().map(c ->
|
||||||
|
SwitchCase.of(
|
||||||
|
c.caseValue(),
|
||||||
|
relabel(c.target(), cob))).toList());
|
||||||
|
case TableSwitchInstruction tsi ->
|
||||||
|
cob.tableSwitchInstruction(
|
||||||
|
tsi.lowValue(),
|
||||||
|
tsi.highValue(),
|
||||||
|
relabel(tsi.defaultTarget(), cob),
|
||||||
|
tsi.cases().stream().map(c ->
|
||||||
|
SwitchCase.of(
|
||||||
|
c.caseValue(),
|
||||||
|
relabel(c.target(), cob))).toList());
|
||||||
|
case LabelTarget lt ->
|
||||||
|
cob.labelBinding(
|
||||||
|
relabel(lt.label(), cob));
|
||||||
|
case ExceptionCatch ec ->
|
||||||
|
cob.exceptionCatch(
|
||||||
|
relabel(ec.tryStart(), cob),
|
||||||
|
relabel(ec.tryEnd(), cob),
|
||||||
|
relabel(ec.handler(), cob),
|
||||||
|
ec.catchType());
|
||||||
|
case LocalVariable lv ->
|
||||||
|
cob.localVariable(
|
||||||
|
lv.slot(),
|
||||||
|
lv.name().stringValue(),
|
||||||
|
lv.typeSymbol(),
|
||||||
|
relabel(lv.startScope(), cob),
|
||||||
|
relabel(lv.endScope(), cob));
|
||||||
|
case LocalVariableType lvt ->
|
||||||
|
cob.localVariableType(
|
||||||
|
lvt.slot(),
|
||||||
|
lvt.name().stringValue(),
|
||||||
|
lvt.signatureSymbol(),
|
||||||
|
relabel(lvt.startScope(), cob),
|
||||||
|
relabel(lvt.endScope(), cob));
|
||||||
|
case CharacterRange chr ->
|
||||||
|
cob.characterRange(
|
||||||
|
relabel(chr.startScope(), cob),
|
||||||
|
relabel(chr.endScope(), cob),
|
||||||
|
chr.characterRangeStart(),
|
||||||
|
chr.characterRangeEnd(),
|
||||||
|
chr.flags());
|
||||||
|
default ->
|
||||||
|
cob.with(coe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,344 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package jdk.internal.classfile.impl;
|
||||||
|
|
||||||
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
|
import jdk.internal.classfile.CodeElement;
|
||||||
|
import jdk.internal.classfile.Label;
|
||||||
|
import jdk.internal.classfile.Opcode;
|
||||||
|
import jdk.internal.classfile.TypeKind;
|
||||||
|
import jdk.internal.classfile.components.CodeStackTracker;
|
||||||
|
import jdk.internal.classfile.instruction.ArrayLoadInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ArrayStoreInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.BranchInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ConstantInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ConvertInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ExceptionCatch;
|
||||||
|
import jdk.internal.classfile.instruction.FieldInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.InvokeDynamicInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.InvokeInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.LabelTarget;
|
||||||
|
import jdk.internal.classfile.instruction.LoadInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.LookupSwitchInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.MonitorInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewMultiArrayInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewObjectInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewPrimitiveArrayInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NewReferenceArrayInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.NopInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.OperatorInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ReturnInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.StackInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.StoreInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.TableSwitchInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.ThrowInstruction;
|
||||||
|
import jdk.internal.classfile.instruction.TypeCheckInstruction;
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public final class CodeStackTrackerImpl implements CodeStackTracker {
|
||||||
|
|
||||||
|
private static record Item(TypeKind type, Item next) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class Stack extends AbstractCollection<TypeKind> {
|
||||||
|
|
||||||
|
private Item top;
|
||||||
|
private int count, realSize;
|
||||||
|
|
||||||
|
Stack(Item top, int count, int realSize) {
|
||||||
|
this.top = top;
|
||||||
|
this.count = count;
|
||||||
|
this.realSize = realSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<TypeKind> iterator() {
|
||||||
|
return new Iterator<TypeKind>() {
|
||||||
|
Item i = top;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeKind next() {
|
||||||
|
if (i == null) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
var t = i.type;
|
||||||
|
i = i.next;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void push(TypeKind type) {
|
||||||
|
top = new Item(type, top);
|
||||||
|
realSize += type.slotSize();
|
||||||
|
count++;
|
||||||
|
if (maxSize != null && realSize > maxSize) maxSize = realSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeKind pop() {
|
||||||
|
var t = top.type;
|
||||||
|
realSize -= t.slotSize();
|
||||||
|
count--;
|
||||||
|
top = top.next;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stack stack = new Stack(null, 0, 0);
|
||||||
|
private Integer maxSize = 0;
|
||||||
|
|
||||||
|
public CodeStackTrackerImpl(TypeKind... initialStack) {
|
||||||
|
for (int i = initialStack.length - 1; i >= 0; i--)
|
||||||
|
push(initialStack[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Collection<TypeKind>> stack() {
|
||||||
|
return Optional.ofNullable(fork());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> maxStackSize() {
|
||||||
|
return Optional.ofNullable(maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<Label, Stack> map = new HashMap<>();
|
||||||
|
|
||||||
|
private void push(TypeKind type) {
|
||||||
|
if (stack != null) {
|
||||||
|
if (type != TypeKind.VoidType) stack.push(type);
|
||||||
|
} else {
|
||||||
|
maxSize = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pop(int i) {
|
||||||
|
if (stack != null) {
|
||||||
|
while (i-- > 0) stack.pop();
|
||||||
|
} else {
|
||||||
|
maxSize = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stack fork() {
|
||||||
|
return stack == null ? null : new Stack(stack.top, stack.count, stack.realSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withStack(Consumer<Stack> c) {
|
||||||
|
if (stack != null) c.accept(stack);
|
||||||
|
else maxSize = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(CodeBuilder cb, CodeElement el) {
|
||||||
|
cb.with(el);
|
||||||
|
switch (el) {
|
||||||
|
case ArrayLoadInstruction i -> {
|
||||||
|
pop(2);push(i.typeKind());
|
||||||
|
}
|
||||||
|
case ArrayStoreInstruction i ->
|
||||||
|
pop(3);
|
||||||
|
case BranchInstruction i -> {
|
||||||
|
if (i.opcode() == Opcode.GOTO || i.opcode() == Opcode.GOTO_W) {
|
||||||
|
map.put(i.target(), stack);
|
||||||
|
stack = null;
|
||||||
|
} else {
|
||||||
|
pop(1);
|
||||||
|
map.put(i.target(), fork());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ConstantInstruction i ->
|
||||||
|
push(i.typeKind());
|
||||||
|
case ConvertInstruction i -> {
|
||||||
|
pop(1);push(i.toType());
|
||||||
|
}
|
||||||
|
case FieldInstruction i -> {
|
||||||
|
switch (i.opcode()) {
|
||||||
|
case GETSTATIC ->
|
||||||
|
push(TypeKind.fromDescriptor(i.type().stringValue()));
|
||||||
|
case GETFIELD -> {
|
||||||
|
pop(1);push(TypeKind.fromDescriptor(i.type().stringValue()));
|
||||||
|
}
|
||||||
|
case PUTSTATIC ->
|
||||||
|
pop(1);
|
||||||
|
case PUTFIELD ->
|
||||||
|
pop(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case InvokeDynamicInstruction i -> {
|
||||||
|
var type = i.typeSymbol();
|
||||||
|
pop(type.parameterCount());
|
||||||
|
push(TypeKind.from(type.returnType()));
|
||||||
|
}
|
||||||
|
case InvokeInstruction i -> {
|
||||||
|
var type = i.typeSymbol();
|
||||||
|
pop(type.parameterCount());
|
||||||
|
if (i.opcode() != Opcode.INVOKESTATIC) pop(1);
|
||||||
|
push(TypeKind.from(type.returnType()));
|
||||||
|
}
|
||||||
|
case LoadInstruction i ->
|
||||||
|
push(i.typeKind());
|
||||||
|
case StoreInstruction i ->
|
||||||
|
pop(1);
|
||||||
|
case LookupSwitchInstruction i -> {
|
||||||
|
map.put(i.defaultTarget(), stack);
|
||||||
|
for (var c : i.cases()) map.put(c.target(), fork());
|
||||||
|
stack = null;
|
||||||
|
}
|
||||||
|
case MonitorInstruction i ->
|
||||||
|
pop(1);
|
||||||
|
case NewMultiArrayInstruction i -> {
|
||||||
|
pop(i.dimensions());push(TypeKind.ReferenceType);
|
||||||
|
}
|
||||||
|
case NewObjectInstruction i ->
|
||||||
|
push(TypeKind.ReferenceType);
|
||||||
|
case NewPrimitiveArrayInstruction i -> {
|
||||||
|
pop(1);push(TypeKind.ReferenceType);
|
||||||
|
}
|
||||||
|
case NewReferenceArrayInstruction i -> {
|
||||||
|
pop(1);push(TypeKind.ReferenceType);
|
||||||
|
}
|
||||||
|
case NopInstruction i -> {}
|
||||||
|
case OperatorInstruction i -> {
|
||||||
|
switch (i.opcode()) {
|
||||||
|
case ARRAYLENGTH, INEG, LNEG, FNEG, DNEG -> pop(1);
|
||||||
|
default -> pop(2);
|
||||||
|
}
|
||||||
|
push(i.typeKind());
|
||||||
|
}
|
||||||
|
case ReturnInstruction i ->
|
||||||
|
stack = null;
|
||||||
|
case StackInstruction i -> {
|
||||||
|
switch (i.opcode()) {
|
||||||
|
case POP -> pop(1);
|
||||||
|
case POP2 -> withStack(s -> {
|
||||||
|
if (s.pop().slotSize() == 1) s.pop();
|
||||||
|
});
|
||||||
|
case DUP -> withStack(s -> {
|
||||||
|
var v = s.pop();s.push(v);s.push(v);
|
||||||
|
});
|
||||||
|
case DUP2 -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
if (v1.slotSize() == 1) {
|
||||||
|
var v2 = s.pop();
|
||||||
|
s.push(v2);s.push(v1);
|
||||||
|
s.push(v2);s.push(v1);
|
||||||
|
} else {
|
||||||
|
s.push(v1);s.push(v1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case DUP_X1 -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
var v2 = s.pop();
|
||||||
|
s.push(v1);s.push(v2);s.push(v1);
|
||||||
|
});
|
||||||
|
case DUP_X2 -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
var v2 = s.pop();
|
||||||
|
if (v2.slotSize() == 1) {
|
||||||
|
var v3 = s.pop();
|
||||||
|
s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
||||||
|
} else {
|
||||||
|
s.push(v1);s.push(v2);s.push(v1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case DUP2_X1 -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
var v2 = s.pop();
|
||||||
|
if (v1.slotSize() == 1) {
|
||||||
|
var v3 = s.pop();
|
||||||
|
s.push(v2);s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
||||||
|
} else {
|
||||||
|
s.push(v1);s.push(v2);s.push(v1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case DUP2_X2 -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
var v2 = s.pop();
|
||||||
|
if (v1.slotSize() == 1) {
|
||||||
|
var v3 = s.pop();
|
||||||
|
if (v3.slotSize() == 1) {
|
||||||
|
var v4 = s.pop();
|
||||||
|
s.push(v2);s.push(v1);s.push(v4);s.push(v3);s.push(v2);s.push(v1);
|
||||||
|
} else {
|
||||||
|
s.push(v2);s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (v2.slotSize() == 1) {
|
||||||
|
var v3 = s.pop();
|
||||||
|
s.push(v1);s.push(v3);s.push(v2);s.push(v1);
|
||||||
|
} else {
|
||||||
|
s.push(v1);s.push(v2);s.push(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case SWAP -> withStack(s -> {
|
||||||
|
var v1 = s.pop();
|
||||||
|
var v2 = s.pop();
|
||||||
|
s.push(v1);s.push(v2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case TableSwitchInstruction i -> {
|
||||||
|
map.put(i.defaultTarget(), stack);
|
||||||
|
for (var c : i.cases()) map.put(c.target(), fork());
|
||||||
|
stack = null;
|
||||||
|
}
|
||||||
|
case ThrowInstruction i ->
|
||||||
|
stack = null;
|
||||||
|
case TypeCheckInstruction i -> {
|
||||||
|
switch (i.opcode()) {
|
||||||
|
case CHECKCAST -> {
|
||||||
|
pop(1);push(TypeKind.ReferenceType);
|
||||||
|
}
|
||||||
|
case INSTANCEOF -> {
|
||||||
|
pop(1);push(TypeKind.IntType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ExceptionCatch i ->
|
||||||
|
map.put(i.handler(), new Stack(new Item(TypeKind.ReferenceType, null), 1, 1));
|
||||||
|
case LabelTarget i ->
|
||||||
|
stack = map.getOrDefault(i.label(), stack);
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user