diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java index ab94d61d6ce..ff65d9aedcd 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java @@ -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 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 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 mapAnnotations(List 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 mapTypeAnnotations(List 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 mapTypeParams(List 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(); - } - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java index 715b018abe8..9fe1d31515b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java @@ -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; - } - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java index 660dc1550fe..a62aa0958f6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java @@ -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 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 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 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); - } - } - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java index 296ba636bbf..a1dac8286e3 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java @@ -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). - * + *

* 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. - * + *

* 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 maxStackSize(); - - final static class CodeStackTrackerImpl implements CodeStackTracker { - - private static record Item(TypeKind type, Item next) { - } - - private final class Stack extends AbstractCollection { - - 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 iterator() { - return new Iterator() { - 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> stack() { - return Optional.ofNullable(fork()); - } - - @Override - public Optional maxStackSize() { - return Optional.ofNullable(maxSize); - } - - private final Map 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 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 -> {} - } - } - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java new file mode 100644 index 00000000000..12ea318684c --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java @@ -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 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 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 mapAnnotations(List 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 mapTypeAnnotations(List 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 mapTypeParams(List 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(); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java new file mode 100644 index 00000000000..66d170e5178 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java @@ -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; + } +} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java new file mode 100644 index 00000000000..47fce6e3429 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java @@ -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 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); + } + } + +} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java new file mode 100644 index 00000000000..c382c559a2e --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java @@ -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 { + + 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 iterator() { + return new Iterator() { + 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> stack() { + return Optional.ofNullable(fork()); + } + + @Override + public Optional maxStackSize() { + return Optional.ofNullable(maxSize); + } + + private final Map 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 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 -> {} + } + } +}