1 /*
  2  * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package helpers;
 24 
 25 import java.lang.constant.ClassDesc;
 26 import java.util.HashMap;
 27 import java.util.List;
 28 import java.util.Random;
 29 import java.lang.classfile.*;
 30 import java.lang.classfile.attribute.*;
 31 import java.lang.classfile.constantpool.*;
 32 import java.lang.classfile.instruction.*;
 33 import java.lang.constant.ModuleDesc;
 34 import java.lang.constant.PackageDesc;
 35 import java.lang.classfile.components.CodeStackTracker;
 36 
 37 class RebuildingTransformation {
 38 
 39     static private Random pathSwitch = new Random(1234);
 40 
 41     static byte[] transform(ClassModel clm) {
 42         return ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(clm.thisClass().asSymbol(), clb -> {
 43             for (var cle : clm) {
 44                 switch (cle) {
 45                     case AccessFlags af -> clb.withFlags(af.flagsMask());
 46                     case Superclass sc -> clb.withSuperclass(sc.superclassEntry().asSymbol());
 47                     case Interfaces i -> clb.withInterfaceSymbols(i.interfaces().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new));
 48                     case ClassFileVersion v -> clb.withVersion(v.majorVersion(), v.minorVersion());
 49                     case FieldModel fm ->
 50                         clb.withField(fm.fieldName().stringValue(), fm.fieldTypeSymbol(), fb -> {
 51                             for (var fe : fm) {
 52                                 switch (fe) {
 53                                     case AccessFlags af -> fb.withFlags(af.flagsMask());
 54                                     case ConstantValueAttribute a -> fb.with(ConstantValueAttribute.of(a.constant().constantValue()));
 55                                     case DeprecatedAttribute a -> fb.with(DeprecatedAttribute.of());
 56                                     case RuntimeInvisibleAnnotationsAttribute a -> fb.with(RuntimeInvisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
 57                                     case RuntimeInvisibleTypeAnnotationsAttribute a -> fb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
 58                                     case RuntimeVisibleAnnotationsAttribute a -> fb.with(RuntimeVisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
 59                                     case RuntimeVisibleTypeAnnotationsAttribute a -> fb.with(RuntimeVisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
 60                                     case SignatureAttribute a -> fb.with(SignatureAttribute.of(Signature.parseFrom(a.asTypeSignature().signatureString())));
 61                                     case SyntheticAttribute a -> fb.with(SyntheticAttribute.of());
 62                                     case CustomAttribute a -> throw new AssertionError("Unexpected custom attribute: " + a.attributeName());
 63                                     case UnknownAttribute a -> throw new AssertionError("Unexpected unknown attribute: " + a.attributeName());
 64                                 }
 65                             }
 66                         });
 67                     case MethodModel mm -> {
 68                         clb.withMethod(mm.methodName().stringValue(), mm.methodTypeSymbol(), mm.flags().flagsMask(), mb -> {
 69                             for (var me : mm) {
 70                                 switch (me) {
 71                                     case AccessFlags af -> mb.withFlags(af.flagsMask());
 72                                     case CodeModel com -> mb.withCode(cob1 ->
 73                                             cob1.transforming(CodeStackTracker.of(), cob2 ->
 74                                             // second pass transforms unbound to unbound instructions
 75                                             cob2.transforming(new CodeRebuildingTransform(), cob3 ->
 76                                             // first pass transforms bound to unbound instructions
 77                                             cob3.transforming(new CodeRebuildingTransform(), cob4 -> {
 78                                                 com.forEach(cob4::with);
 79                                                 com.findAttribute(Attributes.stackMapTable()).ifPresent(cob4::with);
 80                                             }))));
 81                                     case AnnotationDefaultAttribute a -> mb.with(AnnotationDefaultAttribute.of(transformAnnotationValue(a.defaultValue())));
 82                                     case DeprecatedAttribute a -> mb.with(DeprecatedAttribute.of());
 83                                     case ExceptionsAttribute a -> mb.with(ExceptionsAttribute.ofSymbols(a.exceptions().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
 84                                     case MethodParametersAttribute a -> mb.with(MethodParametersAttribute.of(a.parameters().stream().map(mp ->
 85                                             MethodParameterInfo.ofParameter(mp.name().map(Utf8Entry::stringValue), mp.flagsMask())).toArray(MethodParameterInfo[]::new)));
 86                                     case RuntimeInvisibleAnnotationsAttribute a -> mb.with(RuntimeInvisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
 87                                     case RuntimeInvisibleParameterAnnotationsAttribute a -> mb.with(RuntimeInvisibleParameterAnnotationsAttribute.of(a.parameterAnnotations().stream().map(pas -> List.of(transformAnnotations(pas))).toList()));
 88                                     case RuntimeInvisibleTypeAnnotationsAttribute a -> mb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
 89                                     case RuntimeVisibleAnnotationsAttribute a -> mb.with(RuntimeVisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
 90                                     case RuntimeVisibleParameterAnnotationsAttribute a -> mb.with(RuntimeVisibleParameterAnnotationsAttribute.of(a.parameterAnnotations().stream().map(pas -> List.of(transformAnnotations(pas))).toList()));
 91                                     case RuntimeVisibleTypeAnnotationsAttribute a -> mb.with(RuntimeVisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
 92                                     case SignatureAttribute a -> mb.with(SignatureAttribute.of(MethodSignature.parseFrom(a.asMethodSignature().signatureString())));
 93                                     case SyntheticAttribute a -> mb.with(SyntheticAttribute.of());
 94                                     case CustomAttribute a -> throw new AssertionError("Unexpected custom attribute: " + a.attributeName());
 95                                     case UnknownAttribute a -> throw new AssertionError("Unexpected unknown attribute: " + a.attributeName());
 96                                 }
 97                             }
 98                         });
 99                     }
100                     case CompilationIDAttribute a -> clb.with(CompilationIDAttribute.of(a.compilationId().stringValue()));
101                     case DeprecatedAttribute a -> clb.with(DeprecatedAttribute.of());
102                     case EnclosingMethodAttribute a -> clb.with(EnclosingMethodAttribute.of(a.enclosingClass().asSymbol(), a.enclosingMethodName().map(Utf8Entry::stringValue), a.enclosingMethodTypeSymbol()));
103                     case InnerClassesAttribute a -> clb.with(InnerClassesAttribute.of(a.classes().stream().map(ici -> InnerClassInfo.of(
104                             ici.innerClass().asSymbol(),
105                             ici.outerClass().map(ClassEntry::asSymbol),
106                             ici.innerName().map(Utf8Entry::stringValue),
107                             ici.flagsMask())).toArray(InnerClassInfo[]::new)));
108                     case LoadableDescriptorsAttribute a -> clb.with(LoadableDescriptorsAttribute.of(a.loadableDescriptors()));
109                     case ModuleAttribute a -> clb.with(ModuleAttribute.of(a.moduleName().asSymbol(), mob -> {
110                         mob.moduleFlags(a.moduleFlagsMask());
111                         a.moduleVersion().ifPresent(v -> mob.moduleVersion(v.stringValue()));
112                         for (var req : a.requires()) mob.requires(req.requires().asSymbol(), req.requiresFlagsMask(), req.requiresVersion().map(Utf8Entry::stringValue).orElse(null));
113                         for (var exp : a.exports()) mob.exports(exp.exportedPackage().asSymbol(), exp.exportsFlagsMask(), exp.exportsTo().stream().map(ModuleEntry::asSymbol).toArray(ModuleDesc[]::new));
114                         for (var opn : a.opens()) mob.opens(opn.openedPackage().asSymbol(), opn.opensFlagsMask(), opn.opensTo().stream().map(ModuleEntry::asSymbol).toArray(ModuleDesc[]::new));
115                         for (var use : a.uses()) mob.uses(use.asSymbol());
116                         for (var prov : a.provides()) mob.provides(prov.provides().asSymbol(), prov.providesWith().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new));
117                     }));
118                     case ModuleHashesAttribute a -> clb.with(ModuleHashesAttribute.of(a.algorithm().stringValue(),
119                             a.hashes().stream().map(mh -> ModuleHashInfo.of(mh.moduleName().asSymbol(), mh.hash())).toArray(ModuleHashInfo[]::new)));
120                     case ModuleMainClassAttribute a -> clb.with(ModuleMainClassAttribute.of(a.mainClass().asSymbol()));
121                     case ModulePackagesAttribute a -> clb.with(ModulePackagesAttribute.ofNames(a.packages().stream().map(PackageEntry::asSymbol).toArray(PackageDesc[]::new)));
122                     case ModuleResolutionAttribute a -> clb.with(ModuleResolutionAttribute.of(a.resolutionFlags()));
123                     case ModuleTargetAttribute a -> clb.with(ModuleTargetAttribute.of(a.targetPlatform().stringValue()));
124                     case NestHostAttribute a -> clb.with(NestHostAttribute.of(a.nestHost().asSymbol()));
125                     case NestMembersAttribute a -> clb.with(NestMembersAttribute.ofSymbols(a.nestMembers().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
126                     case PermittedSubclassesAttribute a -> clb.with(PermittedSubclassesAttribute.ofSymbols(a.permittedSubclasses().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
127                     case RecordAttribute a -> clb.with(RecordAttribute.of(a.components().stream().map(rci ->
128                             RecordComponentInfo.of(rci.name().stringValue(), rci.descriptorSymbol(), rci.attributes().stream().mapMulti((rca, rcac) -> {
129                                     switch(rca) {
130                                         case RuntimeInvisibleAnnotationsAttribute riaa -> rcac.accept(RuntimeInvisibleAnnotationsAttribute.of(transformAnnotations(riaa.annotations())));
131                                         case RuntimeInvisibleTypeAnnotationsAttribute ritaa -> rcac.accept(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(ritaa.annotations(), null, null)));
132                                         case RuntimeVisibleAnnotationsAttribute rvaa -> rcac.accept(RuntimeVisibleAnnotationsAttribute.of(transformAnnotations(rvaa.annotations())));
133                                         case RuntimeVisibleTypeAnnotationsAttribute rvtaa -> rcac.accept(RuntimeVisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(rvtaa.annotations(), null, null)));
134                                         case SignatureAttribute sa -> rcac.accept(SignatureAttribute.of(Signature.parseFrom(sa.asTypeSignature().signatureString())));
135                                         default -> throw new AssertionError("Unexpected record component attribute: " + rca.attributeName());
136                                     }}).toArray(Attribute[]::new))).toArray(RecordComponentInfo[]::new)));
137                     case RuntimeInvisibleAnnotationsAttribute a -> clb.with(RuntimeInvisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
138                     case RuntimeInvisibleTypeAnnotationsAttribute a -> clb.with(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
139                     case RuntimeVisibleAnnotationsAttribute a -> clb.with(RuntimeVisibleAnnotationsAttribute.of(transformAnnotations(a.annotations())));
140                     case RuntimeVisibleTypeAnnotationsAttribute a -> clb.with(RuntimeVisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), null, null)));
141                     case SignatureAttribute a -> clb.with(SignatureAttribute.of(ClassSignature.parseFrom(a.asClassSignature().signatureString())));
142                     case SourceDebugExtensionAttribute a -> clb.with(SourceDebugExtensionAttribute.of(a.contents()));
143                     case SourceFileAttribute a -> clb.with(SourceFileAttribute.of(a.sourceFile().stringValue()));
144                     case SourceIDAttribute a -> clb.with(SourceIDAttribute.of(a.sourceId().stringValue()));
145                     case SyntheticAttribute a -> clb.with(SyntheticAttribute.of());
146                     case CustomAttribute a -> throw new AssertionError("Unexpected custom attribute: " + a.attributeName());
147                     case UnknownAttribute a -> throw new AssertionError("Unexpected unknown attribute: " + a.attributeName());
148                 }
149             }
150         });
151     }
152 
153     static Annotation[] transformAnnotations(List<Annotation> annotations) {
154         return annotations.stream().map(a -> transformAnnotation(a)).toArray(Annotation[]::new);
155     }
156 
157     static Annotation transformAnnotation(Annotation a) {
158         return Annotation.of(a.classSymbol(), a.elements().stream().map(ae -> AnnotationElement.of(ae.name().stringValue(), transformAnnotationValue(ae.value()))).toArray(AnnotationElement[]::new));
159     }
160 
161     static AnnotationValue transformAnnotationValue(AnnotationValue av) {
162         return switch (av) {
163             case AnnotationValue.OfAnnotation oa -> AnnotationValue.ofAnnotation(transformAnnotation(oa.annotation()));
164             case AnnotationValue.OfArray oa -> AnnotationValue.ofArray(oa.values().stream().map(v -> transformAnnotationValue(v)).toArray(AnnotationValue[]::new));
165             case AnnotationValue.OfString v -> AnnotationValue.of(v.stringValue());
166             case AnnotationValue.OfDouble v -> AnnotationValue.of(v.doubleValue());
167             case AnnotationValue.OfFloat v -> AnnotationValue.of(v.floatValue());
168             case AnnotationValue.OfLong v -> AnnotationValue.of(v.longValue());
169             case AnnotationValue.OfInt v -> AnnotationValue.of(v.intValue());
170             case AnnotationValue.OfShort v -> AnnotationValue.of(v.shortValue());
171             case AnnotationValue.OfChar v -> AnnotationValue.of(v.charValue());
172             case AnnotationValue.OfByte v -> AnnotationValue.of(v.byteValue());
173             case AnnotationValue.OfBoolean v -> AnnotationValue.of(v.booleanValue());
174             case AnnotationValue.OfClass oc -> AnnotationValue.of(oc.classSymbol());
175             case AnnotationValue.OfEnum oe -> AnnotationValue.ofEnum(oe.classSymbol(), oe.constantName().stringValue());
176         };
177     }
178 
179     static TypeAnnotation[] transformTypeAnnotations(List<TypeAnnotation> annotations, CodeBuilder cob, HashMap<Label, Label> labels) {
180         return annotations.stream().map(ta -> TypeAnnotation.of(
181                         transformTargetInfo(ta.targetInfo(), cob, labels),
182                         ta.targetPath().stream().map(tpc -> TypeAnnotation.TypePathComponent.of(tpc.typePathKind(), tpc.typeArgumentIndex())).toList(),
183                         transformAnnotation(ta.annotation()))).toArray(TypeAnnotation[]::new);
184     }
185 
186     static TypeAnnotation.TargetInfo transformTargetInfo(TypeAnnotation.TargetInfo ti, CodeBuilder cob, HashMap<Label, Label> labels) {
187         return switch (ti) {
188             case TypeAnnotation.CatchTarget t -> TypeAnnotation.TargetInfo.ofExceptionParameter(t.exceptionTableIndex());
189             case TypeAnnotation.EmptyTarget t -> TypeAnnotation.TargetInfo.of(t.targetType());
190             case TypeAnnotation.FormalParameterTarget t -> TypeAnnotation.TargetInfo.ofMethodFormalParameter(t.formalParameterIndex());
191             case TypeAnnotation.SupertypeTarget t -> TypeAnnotation.TargetInfo.ofClassExtends(t.supertypeIndex());
192             case TypeAnnotation.ThrowsTarget t -> TypeAnnotation.TargetInfo.ofThrows(t.throwsTargetIndex());
193             case TypeAnnotation.TypeParameterBoundTarget t -> TypeAnnotation.TargetInfo.ofTypeParameterBound(t.targetType(), t.typeParameterIndex(), t.boundIndex());
194             case TypeAnnotation.TypeParameterTarget t -> TypeAnnotation.TargetInfo.ofTypeParameter(t.targetType(), t.typeParameterIndex());
195             case TypeAnnotation.LocalVarTarget t -> TypeAnnotation.TargetInfo.ofVariable(t.targetType(), t.table().stream().map(lvti ->
196                             TypeAnnotation.LocalVarTargetInfo.of(labels.computeIfAbsent(lvti.startLabel(), l -> cob.newLabel()),
197                             labels.computeIfAbsent(lvti.endLabel(), l -> cob.newLabel()), lvti.index())).toList());
198             case TypeAnnotation.OffsetTarget t -> TypeAnnotation.TargetInfo.ofOffset(t.targetType(), labels.computeIfAbsent(t.target(), l -> cob.newLabel()));
199             case TypeAnnotation.TypeArgumentTarget t -> TypeAnnotation.TargetInfo.ofTypeArgument(t.targetType(),
200                             labels.computeIfAbsent(t.target(), l -> cob.newLabel()), t.typeArgumentIndex());
201         };
202     }
203 
204     static List<StackMapFrameInfo.VerificationTypeInfo> transformFrameTypeInfos(List<StackMapFrameInfo.VerificationTypeInfo> infos, CodeBuilder cob, HashMap<Label, Label> labels) {
205         return infos.stream().map(ti -> {
206             return switch (ti) {
207                 case StackMapFrameInfo.SimpleVerificationTypeInfo i -> i;
208                 case StackMapFrameInfo.ObjectVerificationTypeInfo i -> StackMapFrameInfo.ObjectVerificationTypeInfo.of(i.classSymbol());
209                 case StackMapFrameInfo.UninitializedVerificationTypeInfo i -> StackMapFrameInfo.UninitializedVerificationTypeInfo.of(labels.computeIfAbsent(i.newTarget(), l -> cob.newLabel()));
210             };
211         }).toList();
212     }
213 
214     static class CodeRebuildingTransform implements CodeTransform {
215 
216         final HashMap<Label, Label> labels = new HashMap<>();
217 
218         @Override
219         public void accept(CodeBuilder cob, CodeElement coe) {
220             switch (coe) {
221                 case ArrayLoadInstruction i -> {
222                     switch (i.typeKind()) {
223                         case BYTE -> cob.baload();
224                         case SHORT -> cob.saload();
225                         case INT -> cob.iaload();
226                         case FLOAT -> cob.faload();
227                         case LONG -> cob.laload();
228                         case DOUBLE -> cob.daload();
229                         case REFERENCE -> cob.aaload();
230                         case CHAR -> cob.caload();
231                         default -> throw new AssertionError("Should not reach here");
232                     }
233                 }
234                 case ArrayStoreInstruction i -> {
235                     switch (i.typeKind()) {
236                         case BYTE -> cob.bastore();
237                         case SHORT -> cob.sastore();
238                         case INT -> cob.iastore();
239                         case FLOAT -> cob.fastore();
240                         case LONG -> cob.lastore();
241                         case DOUBLE -> cob.dastore();
242                         case REFERENCE -> cob.aastore();
243                         case CHAR -> cob.castore();
244                         default -> throw new AssertionError("Should not reach here");
245                     }
246                 }
247                 case BranchInstruction i -> {
248                     var target = labels.computeIfAbsent(i.target(), l -> cob.newLabel());
249                     switch (i.opcode()) {
250                         case GOTO -> cob.goto_(target);
251                         case GOTO_W -> cob.goto_w(target);
252                         case IF_ACMPEQ -> cob.if_acmpeq(target);
253                         case IF_ACMPNE -> cob.if_acmpne(target);
254                         case IF_ICMPEQ -> cob.if_icmpeq(target);
255                         case IF_ICMPGE -> cob.if_icmpge(target);
256                         case IF_ICMPGT -> cob.if_icmpgt(target);
257                         case IF_ICMPLE -> cob.if_icmple(target);
258                         case IF_ICMPLT -> cob.if_icmplt(target);
259                         case IF_ICMPNE -> cob.if_icmpne(target);
260                         case IFNONNULL -> cob.ifnonnull(target);
261                         case IFNULL -> cob.ifnull(target);
262                         case IFEQ -> cob.ifeq(target);
263                         case IFGE -> cob.ifge(target);
264                         case IFGT -> cob.ifgt(target);
265                         case IFLE -> cob.ifle(target);
266                         case IFLT -> cob.iflt(target);
267                         case IFNE -> cob.ifne(target);
268                         default -> throw new AssertionError("Should not reach here");
269                     }
270                 }
271                 case ConstantInstruction i -> {
272                     if (i.constantValue() == null)
273                         if (pathSwitch.nextBoolean()) cob.aconst_null();
274                         else cob.loadConstant(null);
275                     else switch (i.constantValue()) {
276                         case Integer iVal -> {
277                             if (iVal == 1 && pathSwitch.nextBoolean()) cob.iconst_1();
278                             else if (iVal == 2 && pathSwitch.nextBoolean()) cob.iconst_2();
279                             else if (iVal == 3 && pathSwitch.nextBoolean()) cob.iconst_3();
280                             else if (iVal == 4 && pathSwitch.nextBoolean()) cob.iconst_4();
281                             else if (iVal == 5 && pathSwitch.nextBoolean()) cob.iconst_5();
282                             else if (iVal == -1 && pathSwitch.nextBoolean()) cob.iconst_m1();
283                             else if (iVal >= -128 && iVal <= 127 && pathSwitch.nextBoolean()) cob.bipush(iVal);
284                             else if (iVal >= -32768 && iVal <= 32767 && pathSwitch.nextBoolean()) cob.sipush(iVal);
285                             else cob.loadConstant(iVal);
286                         }
287                         case Long lVal -> {
288                             if (lVal == 0 && pathSwitch.nextBoolean()) cob.lconst_0();
289                             else if (lVal == 1 && pathSwitch.nextBoolean()) cob.lconst_1();
290                             else cob.loadConstant(lVal);
291                         }
292                         case Float fVal -> {
293                             if (fVal == 0.0 && pathSwitch.nextBoolean()) cob.fconst_0();
294                             else if (fVal == 1.0 && pathSwitch.nextBoolean()) cob.fconst_1();
295                             else if (fVal == 2.0 && pathSwitch.nextBoolean()) cob.fconst_2();
296                             else cob.loadConstant(fVal);
297                         }
298                         case Double dVal -> {
299                             if (dVal == 0.0d && pathSwitch.nextBoolean()) cob.dconst_0();
300                             else if (dVal == 1.0d && pathSwitch.nextBoolean()) cob.dconst_1();
301                             else cob.loadConstant(dVal);
302                         }
303                         default -> cob.loadConstant(i.constantValue());
304                     }
305                 }
306                 case ConvertInstruction i -> {
307                     switch (i.fromType()) {
308                         case DOUBLE -> {
309                             switch (i.toType()) {
310                                 case FLOAT -> cob.d2f();
311                                 case INT -> cob.d2i();
312                                 case LONG -> cob.d2l();
313                                 default -> throw new AssertionError("Should not reach here");
314                             }
315                         }
316                         case FLOAT -> {
317                             switch (i.toType()) {
318                                 case DOUBLE -> cob.f2d();
319                                 case INT -> cob.f2i();
320                                 case LONG -> cob.f2l();
321                                 default -> throw new AssertionError("Should not reach here");
322                             }
323                         }
324                         case INT -> {
325                             switch (i.toType()) {
326                                 case BYTE -> cob.i2b();
327                                 case CHAR -> cob.i2c();
328                                 case DOUBLE -> cob.i2d();
329                                 case FLOAT -> cob.i2f();
330                                 case LONG -> cob.i2l();
331                                 case SHORT -> cob.i2s();
332                                 default -> throw new AssertionError("Should not reach here");
333                             }
334                         }
335                         case LONG -> {
336                             switch (i.toType()) {
337                                 case DOUBLE -> cob.l2d();
338                                 case FLOAT -> cob.l2f();
339                                 case INT -> cob.l2i();
340                                 default -> throw new AssertionError("Should not reach here");
341                             }
342                         }
343                         default -> throw new AssertionError("Should not reach here");
344                     }
345                 }
346                 case DiscontinuedInstruction.JsrInstruction i ->
347                     cob.with(DiscontinuedInstruction.JsrInstruction.of(i.opcode(), labels.computeIfAbsent(i.target(), l -> cob.newLabel())));
348                 case DiscontinuedInstruction.RetInstruction i ->
349                     cob.with(DiscontinuedInstruction.RetInstruction.of(i.opcode(), i.slot()));
350                 case FieldInstruction i -> {
351                     if (pathSwitch.nextBoolean()) {
352                         switch (i.opcode()) {
353                             case GETFIELD -> cob.getfield(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
354                             case GETSTATIC -> cob.getstatic(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
355                             case PUTFIELD -> cob.putfield(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
356                             case PUTSTATIC -> cob.putstatic(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
357                             default -> throw new AssertionError("Should not reach here");
358                         }
359                     } else {
360                         switch (i.opcode()) {
361                             case GETFIELD -> cob.getfield(i.field());
362                             case GETSTATIC -> cob.getstatic(i.field());
363                             case PUTFIELD -> cob.putfield(i.field());
364                             case PUTSTATIC -> cob.putstatic(i.field());
365                             default -> throw new AssertionError("Should not reach here");
366                         }
367                     }
368                 }
369                 case InvokeDynamicInstruction i -> {
370                     if (pathSwitch.nextBoolean()) cob.invokedynamic(i.invokedynamic().asSymbol());
371                     else cob.invokedynamic(i.invokedynamic());
372                 }
373                 case InvokeInstruction i -> {
374                     if (pathSwitch.nextBoolean()) {
375                         if (i.isInterface()) {
376                             switch (i.opcode()) {
377                                 case INVOKEINTERFACE -> cob.invokeinterface(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
378                                 case INVOKESPECIAL -> cob.invokespecial(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol(), true);
379                                 case INVOKESTATIC -> cob.invokestatic(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol(), true);
380                                 default -> throw new AssertionError("Should not reach here");
381                             }
382                         } else {
383                             switch (i.opcode()) {
384                                 case INVOKESPECIAL -> cob.invokespecial(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
385                                 case INVOKESTATIC -> cob.invokestatic(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
386                                 case INVOKEVIRTUAL -> cob.invokevirtual(i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
387                                 default -> throw new AssertionError("Should not reach here");
388                             }
389                         }
390                     } else {
391                         switch (i.method()) {
392                             case InterfaceMethodRefEntry en -> {
393                                 switch (i.opcode()) {
394                                         case INVOKEINTERFACE -> cob.invokeinterface(en);
395                                         case INVOKESPECIAL -> cob.invokespecial(en);
396                                         case INVOKESTATIC -> cob.invokestatic(en);
397                                         default -> throw new AssertionError("Should not reach here");
398                                 }
399                             }
400                             case MethodRefEntry en -> {
401                                 switch (i.opcode()) {
402                                         case INVOKESPECIAL -> cob.invokespecial(en);
403                                         case INVOKESTATIC -> cob.invokestatic(en);
404                                         case INVOKEVIRTUAL -> cob.invokevirtual(en);
405                                         default -> throw new AssertionError("Should not reach here");
406                                 }
407                             }
408                             default -> throw new AssertionError("Should not reach here");
409                         }
410                     }
411                 }
412                 case LoadInstruction i -> {
413                     switch (i.typeKind()) {
414                         case INT -> cob.iload(i.slot());
415                         case FLOAT -> cob.fload(i.slot());
416                         case LONG -> cob.lload(i.slot());
417                         case DOUBLE -> cob.dload(i.slot());
418                         case REFERENCE -> cob.aload(i.slot());
419                         default -> throw new AssertionError("Should not reach here");
420                     }
421                 }
422                 case StoreInstruction i -> {
423                     switch (i.typeKind()) {
424                         case INT -> cob.istore(i.slot());
425                         case FLOAT -> cob.fstore(i.slot());
426                         case LONG -> cob.lstore(i.slot());
427                         case DOUBLE -> cob.dstore(i.slot());
428                         case REFERENCE -> cob.astore(i.slot());
429                         default -> throw new AssertionError("Should not reach here");
430                     }
431                 }
432                 case IncrementInstruction i ->
433                     cob.iinc(i.slot(), i.constant());
434                 case LookupSwitchInstruction i ->
435                     cob.lookupswitch(labels.computeIfAbsent(i.defaultTarget(), l -> cob.newLabel()),
436                                      i.cases().stream().map(sc ->
437                                              SwitchCase.of(sc.caseValue(), labels.computeIfAbsent(sc.target(), l -> cob.newLabel()))).toList());
438                 case MonitorInstruction i -> {
439                     switch (i.opcode()) {
440                         case MONITORENTER -> cob.monitorenter();
441                         case MONITOREXIT -> cob.monitorexit();
442                         default -> throw new AssertionError("Should not reach here");
443                     }
444                 }
445                 case NewMultiArrayInstruction i -> {
446                     if (pathSwitch.nextBoolean()) {
447                         cob.multianewarray(i.arrayType().asSymbol(), i.dimensions());
448                     } else {
449                         cob.multianewarray(i.arrayType(), i.dimensions());
450                     }
451                 }
452                 case NewObjectInstruction i -> {
453                     if (pathSwitch.nextBoolean()) {
454                         cob.new_(i.className().asSymbol());
455                     } else {
456                         cob.new_(i.className());
457                     }
458                 }
459                 case NewPrimitiveArrayInstruction i ->
460                     cob.newarray(i.typeKind());
461                 case NewReferenceArrayInstruction i -> {
462                     if (pathSwitch.nextBoolean()) {
463                         cob.anewarray(i.componentType().asSymbol());
464                     } else {
465                         cob.anewarray(i.componentType());
466                     }
467                 }
468                 case NopInstruction i ->
469                     cob.nop();
470                 case OperatorInstruction i -> {
471                     switch (i.opcode()) {
472                         case IADD -> cob.iadd();
473                         case LADD -> cob.ladd();
474                         case FADD -> cob.fadd();
475                         case DADD -> cob.dadd();
476                         case ISUB -> cob.isub();
477                         case LSUB -> cob.lsub();
478                         case FSUB -> cob.fsub();
479                         case DSUB -> cob.dsub();
480                         case IMUL -> cob.imul();
481                         case LMUL -> cob.lmul();
482                         case FMUL -> cob.fmul();
483                         case DMUL -> cob.dmul();
484                         case IDIV -> cob.idiv();
485                         case LDIV -> cob.ldiv();
486                         case FDIV -> cob.fdiv();
487                         case DDIV -> cob.ddiv();
488                         case IREM -> cob.irem();
489                         case LREM -> cob.lrem();
490                         case FREM -> cob.frem();
491                         case DREM -> cob.drem();
492                         case INEG -> cob.ineg();
493                         case LNEG -> cob.lneg();
494                         case FNEG -> cob.fneg();
495                         case DNEG -> cob.dneg();
496                         case ISHL -> cob.ishl();
497                         case LSHL -> cob.lshl();
498                         case ISHR -> cob.ishr();
499                         case LSHR -> cob.lshr();
500                         case IUSHR -> cob.iushr();
501                         case LUSHR -> cob.lushr();
502                         case IAND -> cob.iand();
503                         case LAND -> cob.land();
504                         case IOR -> cob.ior();
505                         case LOR -> cob.lor();
506                         case IXOR -> cob.ixor();
507                         case LXOR -> cob.lxor();
508                         case LCMP -> cob.lcmp();
509                         case FCMPL -> cob.fcmpl();
510                         case FCMPG -> cob.fcmpg();
511                         case DCMPL -> cob.dcmpl();
512                         case DCMPG -> cob.dcmpg();
513                         case ARRAYLENGTH -> cob.arraylength();
514                         default -> throw new AssertionError("Should not reach here");
515                     }
516                 }
517                 case ReturnInstruction i -> {
518                     switch (i.typeKind()) {
519                         case INT -> cob.ireturn();
520                         case FLOAT -> cob.freturn();
521                         case LONG -> cob.lreturn();
522                         case DOUBLE -> cob.dreturn();
523                         case REFERENCE -> cob.areturn();
524                         case VOID -> cob.return_();
525                         default -> throw new AssertionError("Should not reach here");
526                     }
527                 }
528                 case StackInstruction i -> {
529                     switch (i.opcode()) {
530                         case POP -> cob.pop();
531                         case POP2 -> cob.pop2();
532                         case DUP -> cob.dup();
533                         case DUP_X1 -> cob.dup_x1();
534                         case DUP_X2 -> cob.dup_x2();
535                         case DUP2 -> cob.dup2();
536                         case DUP2_X1 -> cob.dup2_x1();
537                         case DUP2_X2 -> cob.dup2_x2();
538                         case SWAP -> cob.swap();
539                         default -> throw new AssertionError("Should not reach here");
540                     }
541                 }
542                 case TableSwitchInstruction i ->
543                     cob.tableswitch(i.lowValue(), i.highValue(),
544                                     labels.computeIfAbsent(i.defaultTarget(), l -> cob.newLabel()),
545                                     i.cases().stream().map(sc ->
546                                             SwitchCase.of(sc.caseValue(), labels.computeIfAbsent(sc.target(), l -> cob.newLabel()))).toList());
547                 case ThrowInstruction i -> cob.athrow();
548                 case TypeCheckInstruction i -> {
549                     if (pathSwitch.nextBoolean()) {
550                         switch (i.opcode()) {
551                             case CHECKCAST -> cob.checkcast(i.type().asSymbol());
552                             case INSTANCEOF -> cob.instanceOf(i.type().asSymbol());
553                             default -> throw new AssertionError("Should not reach here");
554                         }
555                     } else {
556                         switch (i.opcode()) {
557                             case CHECKCAST -> cob.checkcast(i.type());
558                             case INSTANCEOF -> cob.instanceOf(i.type());
559                             default -> throw new AssertionError("Should not reach here");
560                         }
561                     }
562                 }
563                 case CharacterRange pi ->
564                     cob.characterRange(labels.computeIfAbsent(pi.startScope(), l -> cob.newLabel()),
565                                        labels.computeIfAbsent(pi.endScope(), l -> cob.newLabel()),
566                                        pi.characterRangeStart(), pi.characterRangeEnd(), pi.flags());
567                 case ExceptionCatch pi ->
568                     pi.catchType().ifPresentOrElse(
569                             catchType -> cob.exceptionCatch(labels.computeIfAbsent(pi.tryStart(), l -> cob.newLabel()),
570                                                             labels.computeIfAbsent(pi.tryEnd(), l -> cob.newLabel()),
571                                                             labels.computeIfAbsent(pi.handler(), l -> cob.newLabel()),
572                                                             catchType.asSymbol()),
573                             () -> cob.exceptionCatchAll(labels.computeIfAbsent(pi.tryStart(), l -> cob.newLabel()),
574                                                         labels.computeIfAbsent(pi.tryEnd(), l -> cob.newLabel()),
575                                                         labels.computeIfAbsent(pi.handler(), l -> cob.newLabel())));
576                 case LabelTarget pi ->
577                     cob.labelBinding(labels.computeIfAbsent(pi.label(), l -> cob.newLabel()));
578                 case LineNumber pi ->
579                     cob.lineNumber(pi.line());
580                 case LocalVariable pi ->
581                     cob.localVariable(pi.slot(), pi.name().stringValue(), pi.typeSymbol(),
582                                       labels.computeIfAbsent(pi.startScope(), l -> cob.newLabel()),
583                                        labels.computeIfAbsent(pi.endScope(), l -> cob.newLabel()));
584                 case LocalVariableType pi ->
585                     cob.localVariableType(pi.slot(), pi.name().stringValue(),
586                                           Signature.parseFrom(pi.signatureSymbol().signatureString()),
587                                           labels.computeIfAbsent(pi.startScope(), l -> cob.newLabel()),
588                                           labels.computeIfAbsent(pi.endScope(), l -> cob.newLabel()));
589                 case RuntimeInvisibleTypeAnnotationsAttribute a ->
590                     cob.with(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), cob, labels)));
591                 case RuntimeVisibleTypeAnnotationsAttribute a ->
592                     cob.with(RuntimeVisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(a.annotations(), cob, labels)));
593                 case StackMapTableAttribute a ->
594                     cob.with(StackMapTableAttribute.of(a.entries().stream().map(fr ->
595                             StackMapFrameInfo.of(labels.computeIfAbsent(fr.target(), l -> cob.newLabel()),
596                                     transformFrameTypeInfos(fr.locals(), cob, labels),
597                                     transformFrameTypeInfos(fr.stack(), cob, labels))).toList()));
598                 case CustomAttribute a ->
599                     throw new AssertionError("Unexpected custom attribute: " + a.attributeName());
600             }
601         }
602     }
603 }