8306457: Classfile API components implementations should not be exposed

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2023-05-18 06:54:01 +00:00 committed by Adam Sotona
parent f4f5542f8d
commit 3c9ec26370
8 changed files with 989 additions and 852 deletions

View File

@ -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.
*
* 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
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.classfile.components;
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.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.ClassSignature;
import jdk.internal.classfile.ClassTransform;
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.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.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.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.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;
import jdk.internal.classfile.impl.ClassRemapperImpl;
/**
* {@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
* 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.
@ -135,25 +75,22 @@ public sealed interface ClassRemapper extends ClassTransform {
/**
* Access method to internal class mapping function.
* @param desc source class
* @return class target class
* @return target class
*/
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();
/**
* 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();
/**
* 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();
@ -166,330 +103,4 @@ public sealed interface ClassRemapper extends ClassTransform {
return Classfile.build(map(clm.thisClass().asSymbol()),
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();
}
}
}

View File

@ -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.
*
* 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
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.classfile.components;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.lang.reflect.AccessFlag;
import jdk.internal.classfile.AccessFlags;
import jdk.internal.classfile.CodeBuilder;
import jdk.internal.classfile.CodeElement;
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.instruction.LocalVariableType;
import jdk.internal.classfile.impl.CodeLocalsShifterImpl;
/**
* {@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.
* 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}
* 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 methodDescriptor descriptor of the method to construct {@link CodeLocalsShifter} for
* @return new instance of {@link CodeLocalsShifter}
@ -60,65 +50,4 @@ public sealed interface CodeLocalsShifter extends CodeTransform {
fixed += TypeKind.from(param).slotSize();
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;
}
}
}

View File

@ -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.
*
* 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
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.classfile.components;
@ -27,18 +26,9 @@ import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiFunction;
import jdk.internal.classfile.CodeBuilder;
import jdk.internal.classfile.CodeElement;
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.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.impl.CodeRelabelerImpl;
/**
* 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
* {@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
* @return new instance of CodeRelabeler
* Creates a new instance of CodeRelabeler.
* @return a new instance of CodeRelabeler
*/
static CodeRelabeler of() {
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
* @return new instance of CodeRelabeler
* @return a new instance of CodeRelabeler
*/
static CodeRelabeler of(Map<Label, Label> map) {
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.
* @param mapFunction
* @return
* @return a new instance of CodeRelabeler
*/
static CodeRelabeler of(BiFunction<Label, CodeBuilder, Label> mapFunction) {
return new CodeRelabelerImpl(mapFunction);
@ -86,70 +76,4 @@ public sealed interface CodeRelabeler extends CodeTransform {
* @return target label
*/
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);
}
}
}
}

View File

@ -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.
*
* 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
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.classfile.components;
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;
import jdk.internal.classfile.CodeBuilder;
import jdk.internal.classfile.CodeElement;
import jdk.internal.classfile.CodeTransform;
import jdk.internal.classfile.Label;
import jdk.internal.classfile.Opcode;
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
@ -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
* @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 an empty {@linkplain Optional} when the Stack content is unknown
* (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
* 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).
@ -84,291 +74,11 @@ public sealed interface CodeStackTracker extends CodeTransform {
/**
* Returns tracked max stack size.
* 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
* and the actual stack content is unknown.
*
* @return tracked max stack size, or an empty {@linkplain Optional} if tracking has been lost
*/
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 -> {}
}
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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 -> {}
}
}
}