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 
 24 /*
 25  * @test
 26  * @summary Testing ClassFile Verifier.
 27  * @bug 8333812
 28  * @run junit VerifierSelfTest
 29  */
 30 import java.io.IOException;
 31 import java.lang.classfile.constantpool.PoolEntry;
 32 import java.lang.constant.ClassDesc;
 33 
 34 import static java.lang.constant.ConstantDescs.*;
 35 
 36 import java.lang.invoke.MethodHandleInfo;
 37 import java.lang.invoke.MethodHandles;
 38 import java.net.URI;
 39 import java.nio.file.FileSystem;
 40 import java.nio.file.FileSystems;
 41 import java.nio.file.Files;
 42 import java.nio.file.Path;
 43 import java.util.ArrayList;
 44 import java.util.Arrays;
 45 import java.util.LinkedList;
 46 import java.util.List;
 47 import java.util.Optional;
 48 import java.util.stream.Collectors;
 49 import java.util.stream.Stream;
 50 import java.lang.classfile.*;
 51 import java.lang.classfile.attribute.*;
 52 import jdk.internal.classfile.components.ClassPrinter;
 53 import java.lang.classfile.constantpool.Utf8Entry;
 54 import java.lang.constant.ModuleDesc;
 55 
 56 import jdk.internal.classfile.impl.BufWriterImpl;
 57 import jdk.internal.classfile.impl.DirectClassBuilder;
 58 import jdk.internal.classfile.impl.UnboundAttribute;
 59 import org.junit.jupiter.api.Test;
 60 import org.junit.jupiter.params.ParameterizedTest;
 61 import org.junit.jupiter.params.provider.Arguments;
 62 import org.junit.jupiter.params.provider.MethodSource;
 63 
 64 import static org.junit.jupiter.api.Assertions.*;
 65 
 66 class VerifierSelfTest {
 67 
 68     private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/"));
 69 
 70     @Test
 71     void testVerify() throws IOException {
 72         Stream.of(
 73                 Files.walk(JRT.getPath("modules/java.base")),
 74                 Files.walk(JRT.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class")))
 75                     .flatMap(p -> p)
 76                     .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
 77                         try {
 78                             ClassFile.of().verify(path);
 79                         } catch (IOException e) {
 80                             throw new AssertionError(e);
 81                         }
 82                     });
 83     }
 84 
 85     @Test
 86     void testFailed() throws IOException {
 87         Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class");
 88         var cc = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(
 89                 className -> ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null)));
 90         var classModel = cc.parse(path);
 91         byte[] brokenClassBytes = cc.transformClass(classModel,
 92                 (clb, cle) -> {
 93                     if (cle instanceof MethodModel mm) {
 94                         clb.transformMethod(mm, (mb, me) -> {
 95                             if (me instanceof CodeModel cm) {
 96                                 mb.withCode(cob -> cm.forEach(cob));
 97                             }
 98                             else
 99                                 mb.with(me);
100                         });
101                     }
102                     else
103                         clb.with(cle);
104                 });
105         StringBuilder sb = new StringBuilder();
106         if (ClassFile.of().verify(brokenClassBytes).isEmpty()) {
107             throw new AssertionError("expected verification failure");
108         }
109     }
110 
111     @Test
112     void testInvalidAttrLocation() {
113         var cc = ClassFile.of();
114         var bytes = cc.build(ClassDesc.of("InvalidAttrLocationClass"), cb ->
115             ((DirectClassBuilder)cb).writeAttribute(new UnboundAttribute.AdHocAttribute<LocalVariableTableAttribute>(Attributes.localVariableTable()) {
116                 @Override
117                 public void writeBody(BufWriterImpl b) {
118                     b.writeU2(0);
119                 }
120 
121                 @Override
122                 public Utf8Entry attributeName() {
123                     return cb.constantPool().utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TABLE);
124                 }
125             }));
126         assertTrue(cc.verify(bytes).stream().anyMatch(e -> e.getMessage().contains("Invalid LocalVariableTable attribute location")));
127     }
128 
129     @Test
130     void testInvalidClassNameEntry() {
131         var cc = ClassFile.of();
132         var bytes = cc.parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE,
133             0, 0, 0, 0, 0, 2, PoolEntry.TAG_INTEGER, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
134         assertTrue(cc.verify(bytes).stream().anyMatch(e -> e.getMessage().contains("expected ClassEntry")));
135     }
136 
137     @Test
138     void testParserVerification() {
139         var cc = ClassFile.of();
140         var cd_test = ClassDesc.of("ParserVerificationTestClass");
141         var indexes = new Object[9];
142         var clm = cc.parse(cc.build(cd_test, clb -> {
143             clb.withFlags(ClassFile.ACC_INTERFACE | ClassFile.ACC_FINAL);
144             var cp = clb.constantPool();
145             var ce_valid = cp.classEntry(cd_test);
146             var ce_invalid = cp.classEntry(cp.utf8Entry("invalid.class.name"));
147             indexes[0] = ce_invalid.index();
148             var nate_invalid_field = cp.nameAndTypeEntry("field;", CD_int);
149             var nate_invalid_method = cp.nameAndTypeEntry("method;", MTD_void);
150             var bsme = cp.bsmEntry(BSM_INVOKE, List.of());
151             indexes[1] = cp.methodTypeEntry(cp.utf8Entry("invalid method type")).index();
152             indexes[2] = cp.constantDynamicEntry(bsme, nate_invalid_method).index();
153             indexes[3] = cp.invokeDynamicEntry(bsme, nate_invalid_field).index();
154             indexes[4] = cp.fieldRefEntry(ce_invalid, nate_invalid_method).index();
155             indexes[5] = cp.methodRefEntry(ce_invalid, nate_invalid_field).index();
156             indexes[6] = cp.interfaceMethodRefEntry(ce_invalid, nate_invalid_field).index();
157             indexes[7] = cp.methodHandleEntry(MethodHandleInfo.REF_getField, cp.methodRefEntry(cd_test, "method", MTD_void)).index();
158             indexes[8] = cp.methodHandleEntry(MethodHandleInfo.REF_invokeVirtual, cp.fieldRefEntry(cd_test, "field", CD_int)).index();
159             patch(clb,
160                 CompilationIDAttribute.of("12345"),
161                 DeprecatedAttribute.of(),
162                 EnclosingMethodAttribute.of(cd_test, Optional.empty(), Optional.empty()),
163                 InnerClassesAttribute.of(InnerClassInfo.of(cd_test, Optional.of(cd_test), Optional.of("inner"), 0)),
164                 ModuleAttribute.of(ModuleDesc.of("m"), mab -> {}),
165                 ModuleHashesAttribute.of("alg", List.of()),
166                 ModuleMainClassAttribute.of(cd_test),
167                 ModulePackagesAttribute.of(),
168                 ModuleResolutionAttribute.of(0),
169                 ModuleTargetAttribute.of("t"),
170                 NestHostAttribute.of(cd_test),
171                 NestMembersAttribute.ofSymbols(cd_test),
172                 PermittedSubclassesAttribute.ofSymbols(cd_test),
173                 RecordAttribute.of(RecordComponentInfo.of("c", CD_String, patch(
174                         SignatureAttribute.of(Signature.of(CD_String)),
175                         RuntimeVisibleAnnotationsAttribute.of(),
176                         RuntimeInvisibleAnnotationsAttribute.of(),
177                         RuntimeVisibleTypeAnnotationsAttribute.of(),
178                         RuntimeInvisibleTypeAnnotationsAttribute.of()))),
179                 RuntimeVisibleAnnotationsAttribute.of(),
180                 RuntimeInvisibleAnnotationsAttribute.of(),
181                 RuntimeVisibleTypeAnnotationsAttribute.of(),
182                 RuntimeInvisibleTypeAnnotationsAttribute.of(),
183                 SignatureAttribute.of(ClassSignature.of(Signature.ClassTypeSig.of(cd_test))),
184                 SourceDebugExtensionAttribute.of("sde".getBytes()),
185                 SourceFileAttribute.of("ParserVerificationTestClass.java"),
186                 SourceIDAttribute.of("sID"),
187                 SyntheticAttribute.of())
188                     .withInterfaceSymbols(CD_List, CD_List)
189                     .withField("f", CD_String, fb -> patch(fb,
190                             ConstantValueAttribute.of(0),
191                             DeprecatedAttribute.of(),
192                             RuntimeVisibleAnnotationsAttribute.of(),
193                             RuntimeInvisibleAnnotationsAttribute.of(),
194                             RuntimeVisibleTypeAnnotationsAttribute.of(),
195                             RuntimeInvisibleTypeAnnotationsAttribute.of(),
196                             SignatureAttribute.of(Signature.of(CD_String)),
197                             SyntheticAttribute.of()))
198                     .withField("/", CD_int, 0)
199                     .withField("/", CD_int, 0)
200                     .withMethod("m", MTD_void, ClassFile.ACC_ABSTRACT | ClassFile.ACC_STATIC, mb -> patch(mb,
201                             AnnotationDefaultAttribute.of(AnnotationValue.ofInt(0)),
202                             DeprecatedAttribute.of(),
203                             ExceptionsAttribute.ofSymbols(CD_Exception),
204                             MethodParametersAttribute.of(MethodParameterInfo.ofParameter(Optional.empty(), 0)),
205                             RuntimeVisibleAnnotationsAttribute.of(),
206                             RuntimeInvisibleAnnotationsAttribute.of(),
207                             RuntimeVisibleParameterAnnotationsAttribute.of(List.of()),
208                             RuntimeInvisibleParameterAnnotationsAttribute.of(List.of()),
209                             SignatureAttribute.of(MethodSignature.of(MTD_void)),
210                             SyntheticAttribute.of())
211                             .withCode(cob ->
212                                 cob.iconst_0()
213                                    .ifThen(CodeBuilder::nop)
214                                    .return_()
215                                    .with(new CloneAttribute(StackMapTableAttribute.of(List.of())))
216                                    .with(new CloneAttribute(CharacterRangeTableAttribute.of(List.of())))
217                                    .with(new CloneAttribute(LineNumberTableAttribute.of(List.of())))
218                                    .with(new CloneAttribute(LocalVariableTableAttribute.of(List.of())))
219                                    .with(new CloneAttribute(LocalVariableTypeTableAttribute.of(List.of())))))
220                     .withMethod("<>", MTD_void, ClassFile.ACC_NATIVE, mb -> {})
221                     .withMethod("<>", MTD_void, ClassFile.ACC_NATIVE, mb -> {})
222                     .withMethod(INIT_NAME, MTD_void, 0, mb -> {})
223                     .withMethod(CLASS_INIT_NAME, MTD_void, 0, mb -> {});
224                 }));
225         var found = cc.verify(clm).stream().map(VerifyError::getMessage).collect(Collectors.toCollection(LinkedList::new));
226         var expected = """
227                 Invalid class name: invalid.class.name at constant pool index %1$d in class ParserVerificationTestClass
228                 Bad method descriptor: invalid method type at constant pool index %2$d in class ParserVerificationTestClass
229                 not a valid reference type descriptor: ()V at constant pool index %3$d in class ParserVerificationTestClass
230                 Bad method descriptor: I at constant pool index %4$d in class ParserVerificationTestClass
231                 not a valid reference type descriptor: ()V at constant pool index %5$d in class ParserVerificationTestClass
232                 Invalid class name: invalid.class.name at constant pool index %5$d in class ParserVerificationTestClass
233                 Illegal field name method; in class ParserVerificationTestClass at constant pool index %5$d in class ParserVerificationTestClass
234                 Bad method descriptor: I at constant pool index %6$d in class ParserVerificationTestClass
235                 Invalid class name: invalid.class.name at constant pool index %6$d in class ParserVerificationTestClass
236                 Illegal method name field; in class ParserVerificationTestClass at constant pool index %6$d in class ParserVerificationTestClass
237                 Bad method descriptor: I at constant pool index %7$d in class ParserVerificationTestClass
238                 Invalid class name: invalid.class.name at constant pool index %7$d in class ParserVerificationTestClass
239                 Illegal method name field; in class ParserVerificationTestClass at constant pool index %7$d in class ParserVerificationTestClass
240                 not a valid reference type descriptor: ()V at constant pool index %8$d in class ParserVerificationTestClass
241                 Bad method descriptor: I at constant pool index %9$d in class ParserVerificationTestClass
242                 Duplicate interface List in class ParserVerificationTestClass
243                 Illegal field name / in class ParserVerificationTestClass
244                 Duplicate field name / with signature I in class ParserVerificationTestClass
245                 Illegal field name / in class ParserVerificationTestClass
246                 Illegal method name <> in class ParserVerificationTestClass
247                 Duplicate method name <> with signature ()V in class ParserVerificationTestClass
248                 Illegal method name <> in class ParserVerificationTestClass
249                 Interface cannot have a method named <init> in class ParserVerificationTestClass
250                 Method <clinit> is not static in class ParserVerificationTestClass
251                 Multiple CompilationID attributes in class ParserVerificationTestClass
252                 Wrong CompilationID attribute length in class ParserVerificationTestClass
253                 Wrong Deprecated attribute length in class ParserVerificationTestClass
254                 Multiple EnclosingMethod attributes in class ParserVerificationTestClass
255                 Wrong EnclosingMethod attribute length in class ParserVerificationTestClass
256                 Class is both outer and inner class in class ParserVerificationTestClass
257                 Multiple InnerClasses attributes in class ParserVerificationTestClass
258                 Class is both outer and inner class in class ParserVerificationTestClass
259                 Wrong InnerClasses attribute length in class ParserVerificationTestClass
260                 Multiple Module attributes in class ParserVerificationTestClass
261                 Wrong Module attribute length in class ParserVerificationTestClass
262                 Multiple ModuleHashes attributes in class ParserVerificationTestClass
263                 Wrong ModuleHashes attribute length in class ParserVerificationTestClass
264                 Multiple ModuleMainClass attributes in class ParserVerificationTestClass
265                 Wrong ModuleMainClass attribute length in class ParserVerificationTestClass
266                 Multiple ModulePackages attributes in class ParserVerificationTestClass
267                 Wrong ModulePackages attribute length in class ParserVerificationTestClass
268                 Multiple ModuleResolution attributes in class ParserVerificationTestClass
269                 Wrong ModuleResolution attribute length in class ParserVerificationTestClass
270                 Multiple ModuleTarget attributes in class ParserVerificationTestClass
271                 Wrong ModuleTarget attribute length in class ParserVerificationTestClass
272                 Multiple NestHost attributes in class ParserVerificationTestClass
273                 Wrong NestHost attribute length in class ParserVerificationTestClass
274                 Conflicting NestHost and NestMembers attributes in class ParserVerificationTestClass
275                 Multiple NestMembers attributes in class ParserVerificationTestClass
276                 Conflicting NestHost and NestMembers attributes in class ParserVerificationTestClass
277                 Wrong NestMembers attribute length in class ParserVerificationTestClass
278                 PermittedSubclasses attribute in final class ParserVerificationTestClass
279                 Multiple PermittedSubclasses attributes in class ParserVerificationTestClass
280                 PermittedSubclasses attribute in final class ParserVerificationTestClass
281                 Wrong PermittedSubclasses attribute length in class ParserVerificationTestClass
282                 Multiple Record attributes in class ParserVerificationTestClass
283                 Wrong Record attribute length in class ParserVerificationTestClass
284                 Multiple RuntimeVisibleAnnotations attributes in class ParserVerificationTestClass
285                 Wrong RuntimeVisibleAnnotations attribute length in class ParserVerificationTestClass
286                 Multiple RuntimeInvisibleAnnotations attributes in class ParserVerificationTestClass
287                 Wrong RuntimeInvisibleAnnotations attribute length in class ParserVerificationTestClass
288                 Multiple RuntimeVisibleTypeAnnotations attributes in class ParserVerificationTestClass
289                 Wrong RuntimeVisibleTypeAnnotations attribute length in class ParserVerificationTestClass
290                 Multiple RuntimeInvisibleTypeAnnotations attributes in class ParserVerificationTestClass
291                 Wrong RuntimeInvisibleTypeAnnotations attribute length in class ParserVerificationTestClass
292                 Multiple Signature attributes in class ParserVerificationTestClass
293                 Wrong Signature attribute length in class ParserVerificationTestClass
294                 Multiple SourceDebugExtension attributes in class ParserVerificationTestClass
295                 Multiple SourceFile attributes in class ParserVerificationTestClass
296                 Wrong SourceFile attribute length in class ParserVerificationTestClass
297                 Multiple SourceID attributes in class ParserVerificationTestClass
298                 Wrong SourceID attribute length in class ParserVerificationTestClass
299                 Wrong Synthetic attribute length in class ParserVerificationTestClass
300                 Bad constant value type in field ParserVerificationTestClass.f
301                 Multiple ConstantValue attributes in field ParserVerificationTestClass.f
302                 Bad constant value type in field ParserVerificationTestClass.f
303                 Wrong ConstantValue attribute length in field ParserVerificationTestClass.f
304                 Wrong Deprecated attribute length in field ParserVerificationTestClass.f
305                 Multiple RuntimeVisibleAnnotations attributes in field ParserVerificationTestClass.f
306                 Wrong RuntimeVisibleAnnotations attribute length in field ParserVerificationTestClass.f
307                 Multiple RuntimeInvisibleAnnotations attributes in field ParserVerificationTestClass.f
308                 Wrong RuntimeInvisibleAnnotations attribute length in field ParserVerificationTestClass.f
309                 Multiple RuntimeVisibleTypeAnnotations attributes in field ParserVerificationTestClass.f
310                 Wrong RuntimeVisibleTypeAnnotations attribute length in field ParserVerificationTestClass.f
311                 Multiple RuntimeInvisibleTypeAnnotations attributes in field ParserVerificationTestClass.f
312                 Wrong RuntimeInvisibleTypeAnnotations attribute length in field ParserVerificationTestClass.f
313                 Multiple Signature attributes in field ParserVerificationTestClass.f
314                 Wrong Signature attribute length in field ParserVerificationTestClass.f
315                 Wrong Synthetic attribute length in field ParserVerificationTestClass.f
316                 Multiple AnnotationDefault attributes in method ParserVerificationTestClass::m()
317                 Wrong AnnotationDefault attribute length in method ParserVerificationTestClass::m()
318                 Wrong Deprecated attribute length in method ParserVerificationTestClass::m()
319                 Multiple Exceptions attributes in method ParserVerificationTestClass::m()
320                 Wrong Exceptions attribute length in method ParserVerificationTestClass::m()
321                 Multiple MethodParameters attributes in method ParserVerificationTestClass::m()
322                 Wrong MethodParameters attribute length in method ParserVerificationTestClass::m()
323                 Multiple RuntimeVisibleAnnotations attributes in method ParserVerificationTestClass::m()
324                 Wrong RuntimeVisibleAnnotations attribute length in method ParserVerificationTestClass::m()
325                 Multiple RuntimeInvisibleAnnotations attributes in method ParserVerificationTestClass::m()
326                 Wrong RuntimeInvisibleAnnotations attribute length in method ParserVerificationTestClass::m()
327                 Multiple RuntimeVisibleParameterAnnotations attributes in method ParserVerificationTestClass::m()
328                 Wrong RuntimeVisibleParameterAnnotations attribute length in method ParserVerificationTestClass::m()
329                 Multiple RuntimeInvisibleParameterAnnotations attributes in method ParserVerificationTestClass::m()
330                 Wrong RuntimeInvisibleParameterAnnotations attribute length in method ParserVerificationTestClass::m()
331                 Multiple Signature attributes in method ParserVerificationTestClass::m()
332                 Wrong Signature attribute length in method ParserVerificationTestClass::m()
333                 Wrong Synthetic attribute length in method ParserVerificationTestClass::m()
334                 Code attribute in native or abstract method ParserVerificationTestClass::m()
335                 Wrong StackMapTable attribute length in Code attribute for method ParserVerificationTestClass::m()
336                 Wrong CharacterRangeTable attribute length in Code attribute for method ParserVerificationTestClass::m()
337                 Wrong LineNumberTable attribute length in Code attribute for method ParserVerificationTestClass::m()
338                 Wrong LocalVariableTable attribute length in Code attribute for method ParserVerificationTestClass::m()
339                 Wrong LocalVariableTypeTable attribute length in Code attribute for method ParserVerificationTestClass::m()
340                 Multiple StackMapTable attributes in Code attribute for method ParserVerificationTestClass::m()
341                 Multiple Signature attributes in Record component c of class ParserVerificationTestClass
342                 Wrong Signature attribute length in Record component c of class ParserVerificationTestClass
343                 Multiple RuntimeVisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
344                 Wrong RuntimeVisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
345                 Multiple RuntimeInvisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
346                 Wrong RuntimeInvisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
347                 Multiple RuntimeVisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
348                 Wrong RuntimeVisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
349                 Multiple RuntimeInvisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
350                 Wrong RuntimeInvisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
351                 Multiple Signature attributes in Record component c of class ParserVerificationTestClass
352                 Wrong Signature attribute length in Record component c of class ParserVerificationTestClass
353                 Multiple RuntimeVisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
354                 Wrong RuntimeVisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
355                 Multiple RuntimeInvisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
356                 Wrong RuntimeInvisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
357                 Multiple RuntimeVisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
358                 Wrong RuntimeVisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
359                 Multiple RuntimeInvisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
360                 Wrong RuntimeInvisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
361                 Missing Code attribute in ParserVerificationTestClass::<init>() @0
362                 Missing Code attribute in ParserVerificationTestClass::<clinit>() @0
363                 """.formatted(indexes).lines().filter(exp -> !found.remove(exp)).toList();
364         if (!found.isEmpty() || !expected.isEmpty()) {
365             ClassPrinter.toYaml(clm, ClassPrinter.Verbosity.TRACE_ALL, System.out::print);
366             fail("""
367 
368                  Expected:
369                    %s
370 
371                  Found:
372                    %s
373                  """.formatted(expected.stream().collect(Collectors.joining("\n  ")), found.stream().collect(Collectors.joining("\n  "))));
374         }
375     }
376 
377     private static class CloneAttribute extends CustomAttribute<CloneAttribute> {
378         CloneAttribute(Attribute a) {
379             super(new AttributeMapper<CloneAttribute>(){
380                 @Override
381                 public String name() {
382                     return a.attributeName().stringValue();
383                 }
384 
385                 @Override
386                 public CloneAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
387                     throw new UnsupportedOperationException();
388                 }
389 
390                 @Override
391                 public void writeAttribute(BufWriter buf, CloneAttribute attr) {
392                     int start = buf.size();
393                     a.attributeMapper().writeAttribute(buf, a);
394                     buf.writeU1(0); //writes additional byte to the attribute payload
395                     buf.patchInt(start + 2, 4, buf.size() - start - 6);
396                 }
397 
398                 @Override
399                 public AttributeMapper.AttributeStability stability() {
400                     return a.attributeMapper().stability();
401                 }
402             });
403         }
404     }
405 
406     private static <B extends ClassFileBuilder> B patch(B b, Attribute... attrs) {
407         for (var a : attrs) {
408             b.with(a).with(new CloneAttribute(a));
409         }
410         return b;
411     }
412 
413     private static List<Attribute<?>> patch(Attribute... attrs) {
414         var lst = new ArrayList<Attribute<?>>(attrs.length * 2);
415         for (var a : attrs) {
416             lst.add(a);
417             lst.add(new CloneAttribute(a));
418         }
419         return lst;
420     }
421 
422     enum ComparisonInstruction {
423         IF_ACMPEQ(Opcode.IF_ACMPEQ, 2),
424         IF_ACMPNE(Opcode.IF_ACMPNE, 2),
425         IFNONNULL(Opcode.IFNONNULL, 1),
426         IFNULL(Opcode.IFNULL, 1);
427         final Opcode opcode;
428         final int argCount;
429         ComparisonInstruction(Opcode opcode, int argCount) {
430             this.opcode = opcode;
431             this.argCount = argCount;
432         }
433     }
434 
435     enum UninitializeKind {
436         UNINITIALIZED, UNINITIALIZED_THIS
437     }
438 
439     @ParameterizedTest
440     @MethodSource("uninitializedInBytecodeClasses")
441     public void testUninitializedInComparisons(ComparisonInstruction inst, UninitializeKind kind) throws Throwable {
442         var bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ClassDesc.of("Test"), clb -> clb
443                 .withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
444                     StackMapFrameInfo.VerificationTypeInfo uninitializeInfo;
445                     if (kind == UninitializeKind.UNINITIALIZED) {
446                         uninitializeInfo = StackMapFrameInfo.UninitializedVerificationTypeInfo.of(cob.newBoundLabel());
447                         cob.new_(CD_Object);
448                     } else {
449                         uninitializeInfo = StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
450                         cob.aload(0);
451                     }
452 
453                     // Stack: uninitializeInfo
454                     for (int i = 0; i < inst.argCount; i++) {
455                         cob.dup();
456                     }
457                     var dest = cob.newLabel();
458                     cob.branch(inst.opcode, dest)
459                        .nop()
460                        .labelBinding(dest)
461                        .with(StackMapTableAttribute.of(List.of(StackMapFrameInfo.of(dest,
462                                List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS),
463                                List.of(uninitializeInfo)))))
464                        .invokespecial(CD_Object, INIT_NAME, MTD_void);
465                     if (kind == UninitializeKind.UNINITIALIZED) {
466                         // still need to call super constructor
467                         cob.aload(0)
468                            .invokespecial(CD_Object, INIT_NAME, MTD_void);
469                     }
470                     cob.return_();
471                 }));
472         var errors = ClassFile.of().verify(bytes);
473         assertNotEquals(List.of(), errors, () -> errors + " : " + ClassFile.of().parse(bytes).toDebugString());
474         var lookup = MethodHandles.lookup();
475         assertThrows(VerifyError.class, () -> lookup.defineHiddenClass(bytes, true)); // force JVM verification
476     }
477 
478     public static Stream<Arguments> uninitializedInBytecodeClasses() {
479         return Arrays.stream(ComparisonInstruction.values())
480                 .mapMulti((inst, sink) -> {
481                     for (var kind : UninitializeKind.values()) {
482                         sink.accept(Arguments.of(inst, kind));
483                     }
484                 });
485     }
486 }